library(openxlsx)
Warning: package ‘openxlsx’ was built under R version 4.3.3
library(tidyverse)
Warning: package ‘tidyverse’ was built under R version 4.3.3Warning: package ‘ggplot2’ was built under R version 4.3.3Warning: package ‘tibble’ was built under R version 4.3.3Warning: package ‘tidyr’ was built under R version 4.3.3Warning: package ‘readr’ was built under R version 4.3.3Warning: package ‘purrr’ was built under R version 4.3.3Warning: package ‘dplyr’ was built under R version 4.3.3Warning: package ‘stringr’ was built under R version 4.3.3Warning: package ‘forcats’ was built under R version 4.3.3Warning: package ‘lubridate’ was built under R version 4.3.3── Attaching core tidyverse packages ──────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2 ── Conflicts ────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(reshape2)
Warning: package ‘reshape2’ was built under R version 4.3.3
Attaching package: ‘reshape2’
The following object is masked from ‘package:tidyr’:
smiths
library(picante)
Warning: package ‘picante’ was built under R version 4.3.3Loading required package: ape
Warning: package ‘ape’ was built under R version 4.3.3
Attaching package: ‘ape’
The following object is masked from ‘package:dplyr’:
where
Loading required package: vegan
Warning: package ‘vegan’ was built under R version 4.3.3Loading required package: permute
Warning: package ‘permute’ was built under R version 4.3.3Loading required package: lattice
Warning: package ‘lattice’ was built under R version 4.3.3This is vegan 2.6-4
Loading required package: nlme
Warning: package ‘nlme’ was built under R version 4.3.3
Attaching package: ‘nlme’
The following object is masked from ‘package:dplyr’:
collapse
library(tidyr)
library(ggpubr)
Warning: package ‘ggpubr’ was built under R version 4.3.3
Attaching package: ‘ggpubr’
The following object is masked from ‘package:ape’:
rotate
library(mice)
Warning: package ‘mice’ was built under R version 4.3.3
Attaching package: ‘mice’
The following object is masked from ‘package:stats’:
filter
The following objects are masked from ‘package:base’:
cbind, rbind
source("custom_functions.R")
Functions
clinical_boxplot <- function(metadata,variable){
metadata_var <- metadata %>% dplyr::select(all_of(c("PatientID",
variable, "Group"))) %>% as.data.frame()
metadata_var[,variable] <- as.numeric(metadata_var[,variable])
metadata_var_without_na <- metadata_var[!is.na(metadata_var[,variable]),]
colors <- c("#309f87","#f9c675","#F08080","#A00000")
if (length(unique(metadata_var_without_na$Group))==1) colors <- c("#f9c675")
if (!"healthy" %in% unique(metadata_var_without_na$Group)) colors <- c("#f9c675","#F08080","#A00000")
p <- ggplot(metadata_var_without_na) +
geom_boxplot(aes(x=Group, y=!!sym(variable)),outliers = FALSE) +
geom_jitter(width = 0.2,height = 0,aes(x=Group, y=!!sym(variable), color=Group),size=2) +
scale_fill_manual(values=colors) +
scale_color_manual(values=colors) +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0))
return(p)
}
clinical_correlation <- function(metadata,variable,level,
segment="ileum"){
set.seed(123)
level <- tolower(level)
metadata_var <- metadata %>%
dplyr::select(all_of(c("SampleID", "PatientID",
paste0("dys_filtered_",level),
variable, "Group")))
metadata_var[,variable] <- as.numeric(metadata_var[,variable])
metadata_var <- drop_na(metadata_var)
if (segment=="colon"){
n_iterations <- 100
corrs <- list()
for (i in 1:n_iterations){
random_sampled_df <- metadata_var %>%
group_by(PatientID) %>% # Group by PatientID
slice_sample(n = 1) %>% # Randomly pick one row per PatientID
ungroup()
corr <- cor.table(
random_sampled_df[,c(variable,paste0("dys_filtered_",level))],
cor.method="spearman"
)
corr$P <- corr$P[2]
corr$r <- corr$r[2]
corrs[[i]] <- corr
}
r_values_list <- unlist(map(corrs, ~ .x$r)) # List of r matrices
p_values_list <- unlist(map(corrs, ~ .x$P)) # List of P matrices
mean_r <- round(median(r_values_list),2)
p_value <- quantile(p_values_list,0.9)
corr <- list()
corr$r <- mean_r
corr$P <- p_value
} else {
corr <- cor.table(
metadata_var[,c(variable,paste0("dys_filtered_",level))],
cor.method="spearman")
corr$P <- corr$P[2]
corr$r <- round(corr$r[2],2)
}
return(corr)
}
clinical_correlation_abundances <- function(metadata,variable,taxon,level,
segment="ileum"){
set.seed(123)
level <- tolower(level)
metadata_var <- metadata %>% dplyr::select(all_of(c("SampleID", "PatientID",
variable,taxon,
"Group")))
metadata_var[,variable] <- as.numeric(metadata_var[,variable])
metadata_var <- drop_na(metadata_var)
if (segment=="colon"){
n_iterations <- 100
corrs <- list()
for (i in 1:n_iterations){
random_sampled_df <- metadata_var %>%
group_by(PatientID) %>% # Group by PatientID
slice_sample(n = 1) %>% # Randomly pick one row per PatientID
ungroup()
corr <- cor.table(random_sampled_df[,c(variable,taxon)], cor.method="spearman")
corr$P <- corr$P[2]
corr$r <- corr$r[2]
corrs[[i]] <- corr
}
r_values_list <- unlist(map(corrs, ~ .x$r)) # List of r matrices
p_values_list <- unlist(map(corrs, ~ .x$P)) # List of P matrices
mean_r <- round(median(r_values_list),2)
p_value <- quantile(p_values_list,0.9)
#min_p <- min(p_values_list)
#min_p <- round(min_p,2)
#min_p <- ifelse(min_p<=0.05,"< 0.05",ifelse(min_p<=0.01,"< 0.01",ifelse(min_p<=0.001,"< 0.001",paste0("= ",min_p))))
#max_p <- max(p_values_list)
#max_p <- round(max_p,2)
#max_p <- ifelse(max_p<=0.05,"< 0.05",ifelse(max_p<=0.01,"< 0.01",ifelse(max_p<=0.001,"< 0.001",max_p)))
#if (min_p==max_p) combined_df <- min_p
#else {
# max_p <- round(quantile(corr$P,0.9),2)
# combined_df <- ifelse(max_p<=0.05,"< 0.05",ifelse(max_p<=0.01,"< 0.01",ifelse(max_p<=0.001,"< 0.001",paste("=",max_p) )))
#}
corr <- list()
corr$r <- mean_r
#corr$P <- combined_df
corr$P <- p_value
} else {
corr <- cor.table(metadata_var[,c(variable,taxon)], cor.method="spearman")
#max_p <- round(corr$P[2],2)
#max_p <-
# max_p <- ifelse(max_p<=0.05,"< 0.05",ifelse(max_p<=0.01,"< 0.01",ifelse(max_p<=0.001,"< 0.001",paste0("= ",max_p))))
#corr$P <- max_p
corr$r <- round(corr$r[2],2)
corr$P <- corr$P[2]
}
return(corr)
}
clinical_scatter <- function(corr,metadata,variable,level){
level <- tolower(level)
df_for_scatter_plot <- metadata %>%
dplyr::select(all_of(c("SampleID", "PatientID",
paste0("dys_filtered_",level),
variable, "Group")))
df_for_scatter_plot[,variable] <- as.numeric(df_for_scatter_plot[,variable])
df_for_scatter_plot <- drop_na(df_for_scatter_plot)
colors <- c("#309f87","#f9c675","#F08080","#A00000")
if (!"healthy" %in% unique(df_for_scatter_plot$Group)) colors <- c("#f9c675","#F08080","#A00000")
r_corr <- corr$r
p_corr <- corr$P
p_corr <- ifelse(p_corr < 0.001, "<0.001", ifelse(p_corr < 0.01, "<0.01", ifelse(p_corr < 0.05, "<0.05", paste0("=",round(p_corr,3)))))
x_var <- paste0("dys_filtered_",level)
x_position <- (max(df_for_scatter_plot[,x_var])) - 0.2*(max(df_for_scatter_plot[,x_var]) - min(df_for_scatter_plot[,x_var]))
y_position <- max(df_for_scatter_plot[,variable]) - 0.05*(max(df_for_scatter_plot[,variable]) - min(df_for_scatter_plot[,variable]))
labels <- paste0("r = ",r_corr, "\\np ",p_corr)
p <- ggplot(df_for_scatter_plot) +
geom_point(aes(x=!!sym(x_var), y=!!sym(variable), color=Group)) +
geom_smooth(aes(x=!!sym(x_var), y=!!sym(variable)),method=lm, se=TRUE) +
geom_text(aes(x=x_position, y=y_position),
label=gsub("\\\\n", "\n", labels),hjust = 0) +
scale_color_manual(values=colors) +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0)) +
xlab("MDI")
return(p)
}
clinical_scatter_abundances <- function(corr,metadata,variable,taxon,level,
size=4){
level <- tolower(level)
df_for_scatter_plot <- metadata %>% dplyr::select(all_of(c("SampleID", "PatientID",
taxon,
variable, "Group")))
df_for_scatter_plot[,variable] <- as.numeric(df_for_scatter_plot[,variable])
df_for_scatter_plot <- drop_na(df_for_scatter_plot)
colors <- c("#309f87","#f9c675","#F08080","#A00000")
if (!"healthy" %in% unique(df_for_scatter_plot$Group)) colors <- c("#f9c675","#F08080","#A00000")
x_var <- taxon
r_corr <- corr$r
p_corr <- corr$P
x_position <- (max(df_for_scatter_plot[,x_var])) - 0.2*(max(df_for_scatter_plot[,x_var]) - min(df_for_scatter_plot[,x_var]))
y_position <- max(df_for_scatter_plot[,variable]) - 0.05*(max(df_for_scatter_plot[,variable]) - min(df_for_scatter_plot[,variable]))
labels <- paste0("r = ",r_corr, "\\np ",p_corr)
p <- ggplot(df_for_scatter_plot) +
geom_point(aes(x=!!sym(x_var), y=!!sym(variable), color=Group)) +
geom_smooth(aes(x=!!sym(x_var), y=!!sym(variable)),method=lm, se=TRUE) +
geom_text(aes(x=x_position, y=y_position,label=gsub("\\\\n", "\n", labels)),size=size) +
scale_color_manual(values=colors) +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0))
return(p)
}
clinical_contingency <- function(metadata,variable){
metadata_var <- metadata %>% dplyr::select(all_of(c("PatientID",
variable, "Group"))) %>% as.data.frame()
metadata_var_without_na <- metadata_var[!is.na(metadata_var[,variable]),]
cont_table <- as.data.frame(table(metadata_var_without_na$Group, metadata_var_without_na[,variable]))
p <- ggplot(cont_table, aes(x = Var1, y = Var2, fill = Freq)) +
geom_tile(color = "white") + # Tile background color
geom_text(aes(label = Freq), color = "black", size = 6) + # Add numbers
scale_fill_gradient(low = "white", high = "blue") + # Color gradient
labs(title = "Contingency Table with Counts",
x = "Group", y = variable) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
return(p)
}
clinical_boxplot_categorical <- function(metadata, variable,level){
level=tolower(level)
metadata_var <- metadata %>% dplyr::select(all_of(c("SampleID", "PatientID",
paste0("dys_filtered_",level),
variable, "Group")))
metadata_var <- drop_na(metadata_var)
metadata_var[,variable] <- as.factor(metadata_var[,variable])
y_var <- paste0("dys_filtered_",level)
p <- ggplot(metadata_var) +
geom_boxplot(aes(x=!!sym(variable), y=!!sym(y_var)),outliers = FALSE) +
geom_jitter(width = 0.2,height = 0,aes(x=!!sym(variable), y=!!sym(y_var),
color=Group),size=2) +
scale_fill_manual(values=c("#f9c675","#F08080","#A00000")) +
scale_color_manual(values=c("#f9c675","#F08080","#A00000")) +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0))
return(p)
p
}
subprepare_for_heatmap <- function(corrs_segment,MDI=TRUE){
corrs_segment <- corrs_segment[!grepl("_log_",names(corrs_segment))]
names(corrs_segment) <- gsub("score_","",names(corrs_segment))
# Example list structure for demonstration
# Initialize empty lists for r and P values
r_values <- list()
p_values <- list()
# Extract segment and clinical variables
for (name in names(corrs_segment)) {
if (MDI) {
clinical_var <- sub("^(.*?_){2}", "", name)
if (all(grepl("ileum",names(corrs_segment)))) {
segment <- "MDI terminal ileum"
} else if (all(grepl("colon",names(corrs_segment)))) {
segment <- "MDI colon"
} else (message("PROBLEM with segment"))
}
else {
segment <- sub("^(.*?_){3}", "", name)
clinical_var <- sub(segment,"", sub("^(.*?_){2}", "", name))
clinical_var <- gsub("_","",clinical_var)
}
# Fill the r and P lists
if (!is.null(corrs_segment[[name]]$r)) {
r_values[[segment]][clinical_var] <- corrs_segment[[name]]$r
}
if (!is.null(corrs_segment[[name]]$P)) {
p_values[[segment]][clinical_var] <- corrs_segment[[name]]$P
}
}
# Convert the lists to data frames
r_df <- do.call(rbind, lapply(r_values, function(x) setNames(as.data.frame(t(x)), names(x))))
p_df <- do.call(rbind, lapply(p_values, function(x) setNames(as.data.frame(t(x)), names(x))))
# Add row names as segment names
rownames(r_df) <- names(r_values)
rownames(p_df) <- names(p_values)
if (MDI) pd_df_corrected <- as.data.frame(p.adjust(p_df,method="BH"))
else pd_df_corrected <- as.data.frame(apply(p_df,2,function(x) p.adjust(x, method="BH")))
p_df_sig <- pd_df_corrected
p_df_sig[,] <- ""
p_df_sig[pd_df_corrected < 0.05] <- "*"
p_df_sig[pd_df_corrected < 0.01] <- "**"
p_df_sig[pd_df_corrected < 0.001] <- "***"
if (MDI) p_df_sig <- t(p_df_sig) %>%
as.data.frame() %>%
`rownames<-`(segment)
return(list(p_df_sig,r_df))
}
prepare_for_heatmap <- function(corrs_ileum,corrs_colon){
ileum_list <- subprepare_for_heatmap(corrs_ileum)
colon_list <- subprepare_for_heatmap(corrs_colon)
p_df_sig <- rbind(ileum_list[[1]],colon_list[[1]])
r_df <- rbind(ileum_list[[2]],colon_list[[2]])
return(list(p_df_sig,r_df))
}
Import clinical data
# clinical metadata
metadata_clinical <- read.csv("../../data/clinical_data/clinical_metadata_cz.csv")
metadata_clinical$PatientID <- as.character(metadata_clinical$PatientID)
# DYSBIOSIS
metadata_dysbiosis <- read.csv("../../data/clinical_data/dysbiosis_metadata.csv") %>%
dplyr::filter(Country=="CZ")
# ALPHA DIVERSITY
metadata_alpha_ileum <- read.csv(
"../results/Q1/alpha_diversity/alpha_indices_terminal_ileum.csv") %>%
dplyr::filter(Country=="CZ")
metadata_alpha_colon <- read.csv(
"../results/Q1/alpha_diversity/alpha_indices_colon.csv") %>%
dplyr::filter(Country=="CZ")
metadata_alpha <- rbind(metadata_alpha_ileum,metadata_alpha_colon) %>%
mutate(PatientID=Patient) %>%
dplyr::select(-c(Patient, Group))
# MERGING
metadata_cz <- full_join(metadata_clinical, metadata_dysbiosis, by=c("SampleID","Matrix","PatientID","Group","Country"))
metadata_cz <- full_join(metadata_cz, metadata_alpha, by=c("SampleID","PatientID","Country"))
metadata_cz$Group <- factor(metadata_cz$Group,levels = c("healthy","pre_ltx","non-rPSC","rPSC"))
variables <- c("PatientID", "Group",
"Bilirubin", "Albumin",
"ALP", "Platelets",
"Fecal.calprotectin",
"MAYO_PSC","AOM_score",
"APRI_score",
"FIB4_score",
"Nancy_max","eMAYO","MAYO_dai",
"Creatinine",
"Na",
"ALT",
"AST",
"GGT", "INR")
metadata_cz$Fecal.calprotectin[metadata_cz$Group=="healthy"] <- NA
metadata_cz$INR[metadata_cz$Group=="healthy"] <- NA
metadata_cz$Fecal.calprotectin[metadata_cz$Fecal.calprotectin==">6000"] <- 6000
metadata_cz$Fecal.calprotectin <- as.numeric(metadata_cz$Fecal.calprotectin)
metadata_ileum <- metadata_cz %>% subset(Matrix=="TI") %>% as.data.frame()
metadata_colon <- metadata_cz %>% subset(Matrix!="TI") %>% as.data.frame()
# TO DO!!!!!!!!!!!!!! nefunguje ako ma
metadata_for_boxplots <- metadata_cz[,variables] %>%
group_by(PatientID) %>%
distinct(PatientID, .keep_all = TRUE) %>%
as.data.frame()
Import microbiome data
Data Import
Importing ASV, taxa and metadata tables for both Czech and Norway
samples.
Czech
path = "../../data/analysis_ready_data/ikem/"
asv_tab_ikem <- as.data.frame(fread(file.path(path,"asv_table_ikem.csv"),
check.names = FALSE))
taxa_tab_ikem <- as.data.frame(fread(file.path(path,"taxa_table_ikem.csv"),
check.names = FALSE))
metadata_ikem <- as.data.frame(fread(file.path(path,"metadata_ikem.csv"),
check.names = FALSE))
Spliting to segments
Terminal ileum
ileum_data <- merging_data(asv_tab_1=asv_tab_ikem,
asv_tab_2=NULL,
taxa_tab_1=taxa_tab_ikem,
taxa_tab_2=NULL,
metadata_1=metadata_ikem,
metadata_2=NULL,
segment="TI",Q="clinical")
Removing 1498 ASV(s)
ileum_asv_tab <- ileum_data[[1]]
ileum_taxa_tab <- ileum_data[[2]]
ileum_metadata <- ileum_data[[3]]
Colon
colon_data <- merging_data(asv_tab_1=asv_tab_ikem,
asv_tab_2=NULL,
taxa_tab_1=taxa_tab_ikem,
taxa_tab_2=NULL,
metadata_1=metadata_ikem,
metadata_2=NULL,
segment="colon",Q="clinical")
Removing 739 ASV(s)
colon_asv_tab <- colon_data[[1]]
colon_taxa_tab <- colon_data[[2]]
colon_metadata <- colon_data[[3]]
Filtering
Rules: - prevalence > 5% (per group) - nearZeroVar with default
settings - sequencing depth > 5000 - taxonomic assignment at least
order
Sequencing depth
data_filt <- seq_depth_filtering(ileum_asv_tab,
ileum_taxa_tab,
ileum_metadata,
seq_depth_threshold = 10000)
Removing 73 ASV(s)
filt_ileum_asv_tab <- data_filt[[1]]; alpha_ileum_asv_tab <- filt_ileum_asv_tab
filt_ileum_taxa_tab <- data_filt[[2]]; alpha_ileum_taxa_tab <- filt_ileum_taxa_tab
filt_ileum_metadata <- data_filt[[3]]; alpha_ileum_metadata <- filt_ileum_metadata
seq_step <- dim(filt_ileum_asv_tab)[1]
NearZeroVar
data_filt <- nearzerovar_filtering(filt_ileum_asv_tab,
filt_ileum_taxa_tab,
filt_ileum_metadata)
filt_ileum_asv_tab <- data_filt[[1]]
filt_ileum_taxa_tab <- data_filt[[2]]
nearzero_step <- dim(filt_ileum_asv_tab)[1]
DYSBIOSIS INDEX
Ileum
All indices
metadata_ileum_melted <- melt(metadata_ileum %>%
dplyr::select("Group",
"dys_unfiltered_asv",
"dys_unfiltered_genus",
"dys_filtered_asv",
"dys_filtered_genus"))
Warning: c("The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(metadata_ileum %>% dplyr::select(\"Group\", \"dys_unfiltered_asv\", ). In the next version, this warning will become an error.",
"The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt( \"dys_unfiltered_genus\", \"dys_filtered_asv\", \"dys_filtered_genus\")). In the next version, this warning will become an error."
)Using Group as id variables
p_il <- ggplot(metadata_ileum_melted) +
geom_boxplot(aes(x=Group, y=value),outliers = FALSE) +
geom_jitter(width = 0.2,height = 0,aes(x=Group, y=value, color=Group),
size=2) +
facet_wrap(~variable, ncol = 4,scales = "free") +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0)) +
scale_fill_manual(values=c("#309f87","#f9c675","#F08080","#A00000")) +
scale_color_manual(values=c("#309f87","#f9c675","#F08080","#A00000"))
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
p_il

FILTERED GENUS
dys_limit <- max(metadata_ileum$dys_filtered_genus,na.rm = TRUE) + 0.6*max(metadata_ileum$dys_filtered_genus,na.rm = TRUE)
dys_min_limit <- min(metadata_ileum$dys_filtered_genus)
p_il_dys_genus <- ggplot(metadata_ileum %>%
mutate(`MDI`=dys_filtered_genus)) +
geom_boxplot(aes(x=Group, y=`MDI`),
outliers = FALSE) +
geom_jitter(width = 0.3,height = 0,
aes(x=Group, y=`MDI`, color=Group,shape=Country),
size=1.5) +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0)) +
ylim(dys_min_limit,dys_limit) +
scale_fill_manual(values=c("#309f87","#f9c675","#F08080","#A00000")) +
scale_color_manual(values=c("#309f87","#f9c675","#F08080","#A00000")) +
theme(axis.text.x = element_text(angle = 45,face = "bold",vjust = 0.5))
p_il_dys_genus

Colon
All indices
colon_metadata_melted <- melt(metadata_colon %>%
dplyr::select("Group",
"dys_unfiltered_asv",
"dys_unfiltered_genus",
"dys_filtered_asv",
"dys_filtered_genus"))
Warning: c("The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(metadata_colon %>% dplyr::select(\"Group\", \"dys_unfiltered_asv\", ). In the next version, this warning will become an error.",
"The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt( \"dys_unfiltered_genus\", \"dys_filtered_asv\", \"dys_filtered_genus\")). In the next version, this warning will become an error."
)Using Group as id variables
p_col <- ggplot(colon_metadata_melted) +
geom_boxplot(aes(x=Group, y=value),outliers = FALSE) +
geom_jitter(width = 0.2,height = 0,
aes(x=Group, y=value, color=Group),
size=2) +
facet_wrap(~variable, ncol = 4,scales = "free") +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0)) +
scale_fill_manual(values=c("#309f87","#f9c675","#F08080","#A00000")) +
scale_color_manual(values=c("#309f87","#f9c675","#F08080","#A00000"))
p_col

FILTERED GENUS
dys_limit <- max(metadata_colon$dys_filtered_genus,na.rm = TRUE) + 0.6*max(metadata_colon$dys_filtered_genus,na.rm = TRUE)
dys_min_limit <- min(metadata_colon$dys_filtered_genus)
p_col_dys_genus <- ggplot(metadata_colon %>%
mutate(`MDI`=dys_filtered_genus)) +
geom_boxplot(aes(x=Group, y=`MDI`),outliers = FALSE) +
geom_jitter(width = 0.3,height = 0,
aes(x=Group, y=`MDI`, color=Group,shape=Country),
size=1) +
theme_minimal() +
theme(panel.border = element_rect(color = "black", fill = NA, size = 0))+
ylim(dys_min_limit,dys_limit) +
scale_fill_manual(values=c("#309f87","#f9c675","#F08080","#A00000")) +
scale_color_manual(values=c("#309f87","#f9c675","#F08080","#A00000")) +
theme(axis.text.x = element_text(angle = 45,face = "bold",vjust = 0.5))
p_col_dys_genus

Linear models
Ileum
results_model <- pairwise.lm(
formula = "dys_filtered_genus ~ Group",
factors=metadata_ileum$Group,
data=metadata_ileum)
knitr::kable(results_model[[1]][,c("Estimate", "p.adj","sig")],
digits=3,caption="Results of linear modeling")
Results of linear modeling
| healthy vs Groupnon-rPSC |
17.097 |
0.000 |
*** |
| healthy vs GrouprPSC |
22.812 |
0.000 |
*** |
| healthy vs Grouppre_ltx |
25.244 |
0.000 |
*** |
| non-rPSC vs GrouprPSC |
5.715 |
0.117 |
|
| pre_ltx vs Groupnon-rPSC |
-8.148 |
0.040 |
* |
| pre_ltx vs GrouprPSC |
-2.433 |
0.613 |
|
results_model <- pairwise.lmer(
formula = "dys_filtered_genus ~ Group + (1|PatientID)",
factors=metadata_colon$Group,
data=metadata_colon)
knitr::kable(results_model[[1]][,c("Estimate", "p.adj","sig")],
digits=3,caption="Results of linear mixed-effect modeling")
Results of linear mixed-effect modeling
| healthy vs Groupnon-rPSC |
25.829 |
0.000 |
*** |
| healthy vs GrouprPSC |
40.137 |
0.000 |
*** |
| healthy vs Grouppre_ltx |
37.534 |
0.000 |
*** |
| non-rPSC vs GrouprPSC |
14.320 |
0.001 |
*** |
| pre_ltx vs Groupnon-rPSC |
-11.696 |
0.007 |
** |
| pre_ltx vs GrouprPSC |
2.664 |
0.621 |
|
NA
dys <- ggarrange(p_il + ggtitle("Ileum"),
p_col + ggtitle("Colon"),nrow = 2)
pdf("results/clinical/dysbiosis_index_boxplots.pdf",
height = 8,width =15)
dys
dev.off()
ALPHA DIVERSITY
alpha_div_plots <- list()
alpha_boxplots <- list()
corrs <- list()
Ileum
ileum_metadata_alpha_final <- metadata_ileum %>%
dplyr::select("SampleID", "PatientID","Group",
"Observe","Shannon","Simpson","Pielou",
"dys_filtered_asv", "dys_filtered_genus")
variables <- c("Observe","Shannon","Simpson","Pielou")
for (clinical_variable in variables){
# ILEUM
# boxplot
p <- clinical_boxplot(ileum_metadata_alpha_final,
variable=clinical_variable)
alpha_boxplots[[paste0("ileum_",clinical_variable)]] <- p
# correlation
## ASV
level="ASV"
corr <- clinical_correlation(
ileum_metadata_alpha_final,
clinical_variable,
level)
corrs[[paste0("ileum_asv_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
ileum_metadata_alpha_final,
clinical_variable,
level)
alpha_div_plots[[paste0("ileum_asv_",clinical_variable)]] <- p
## Genus
level="genus"
corr <- clinical_correlation(ileum_metadata_alpha_final,
clinical_variable,
level)
corrs[[paste0("ileum_genus_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
ileum_metadata_alpha_final,
clinical_variable,
level)
alpha_div_plots[[paste0("ileum_genus_",clinical_variable)]] <- p
}
Boxplot
alpha_boxplots_ileum <- ggarrange(
plotlist = alpha_boxplots[grepl("ileum",names(alpha_boxplots))],
ncol = 4,
common.legend = TRUE,
legend = "right")
alpha_boxplots_ileum

Colon
colon_metadata_alpha_final <- metadata_colon %>%
dplyr::select("SampleID", "PatientID", "Group",
"Observe","Shannon","Simpson","Pielou",
"dys_filtered_asv","dys_filtered_genus")
variables <- c("Observe","Shannon","Simpson","Pielou")
for (clinical_variable in variables){
# COLUMN
p <- clinical_boxplot(colon_metadata_alpha_final,
variable=clinical_variable)
alpha_boxplots[[paste0("colon_",clinical_variable)]] <- p
# correlation
## ASV
level="ASV"
corr <- clinical_correlation(colon_metadata_alpha_final,
clinical_variable,
level,
segment="colon")
corrs[[paste0("colon_asv_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
colon_metadata_alpha_final,
clinical_variable,
level)
alpha_div_plots[[paste0("colon_asv_",clinical_variable)]] <- p
## Genus
level="genus"
corr <- clinical_correlation(colon_metadata_alpha_final,
clinical_variable,
level,
segment="colon")
corrs[[paste0("colon_genus_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
colon_metadata_alpha_final,
clinical_variable,
level)
alpha_div_plots[[paste0("colon_genus_",clinical_variable)]] <- p
}
Boxplot
alpha_boxplots_colon <- ggarrange(
plotlist = alpha_boxplots[grepl("colon",names(alpha_boxplots))],
ncol = 4,
common.legend = TRUE,
legend = "right")
alpha_boxplots_colon

alpha_boxplot <- ggarrange(alpha_boxplots_ileum,
alpha_boxplots_colon,
nrow = 2)
pdf("results/clinical/alpha_diversity_boxplots.pdf",height = 7,width =17)
alpha_boxplot
dev.off()
Scatterplots
ASV
alpha_dys_asv <- ggarrange(
plotlist = alpha_div_plots[grepl("asv",names(alpha_div_plots))],
ncol=4,
nrow=2,
common.legend = TRUE,
legend="right")
Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'
alpha_dys_asv

Genus
alpha_dys_genus <- ggarrange(
plotlist = alpha_div_plots[grepl("genus",names(alpha_div_plots))],
ncol=4,
nrow=2,
common.legend = TRUE,
legend="right")
Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 163 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 346 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'
alpha_dys_genus

Saving plots
pdf("results/clinical/alpha_diversity.pdf",height = 8,width =18)
alpha_dys_asv
dev.off()
pdf("results/clinical/alpha_diversity_genus.pdf",height = 8,width =18)
alpha_dys_genus
dev.off()
Linear models
Richness
Ileum
results_model <- pairwise.lm(formula = "Observe ~ Group",
factors=metadata_ileum$Group,
data=metadata_ileum)
knitr::kable(results_model[[1]][,c("Estimate", "p.adj","sig")],
digits=3,caption="Results of linear modeling")
Results of linear modeling
| healthy vs Groupnon-rPSC |
-25.110 |
0.017 |
* |
| healthy vs GrouprPSC |
-37.475 |
0.011 |
* |
| healthy vs Grouppre_ltx |
-53.642 |
0.001 |
*** |
| non-rPSC vs GrouprPSC |
-12.365 |
0.326 |
|
| pre_ltx vs Groupnon-rPSC |
28.532 |
0.028 |
* |
| pre_ltx vs GrouprPSC |
16.167 |
0.326 |
|
Colon
results_model <- pairwise.lmer(
formula = "Observe ~ Group + (1|Patient)",
factors=metadata_colon$Group,
data=metadata_colon %>% mutate(Patient=PatientID))
knitr::kable(results_model[[1]][,c("Estimate", "p.adj","sig")],
digits=3,caption="Results of linear modeling")
Results of linear modeling
| healthy vs Groupnon-rPSC |
-23.271 |
0.013 |
* |
| healthy vs GrouprPSC |
-40.520 |
0.003 |
** |
| healthy vs Grouppre_ltx |
-56.232 |
0.000 |
*** |
| non-rPSC vs GrouprPSC |
-17.144 |
0.195 |
|
| pre_ltx vs Groupnon-rPSC |
32.962 |
0.013 |
* |
| pre_ltx vs GrouprPSC |
15.641 |
0.339 |
|
Shannon
Ileum
results_model <- pairwise.lm(formula = "Shannon ~ Group",
factors=metadata_ileum$Group,
data=metadata_ileum)
knitr::kable(results_model[[1]][,c("Estimate", "p.adj","sig")],
digits=3,caption="Results of linear modeling")
Results of linear modeling
| healthy vs Groupnon-rPSC |
-0.109 |
0.417 |
|
| healthy vs GrouprPSC |
-0.212 |
0.243 |
|
| healthy vs Grouppre_ltx |
-0.505 |
0.032 |
* |
| non-rPSC vs GrouprPSC |
-0.103 |
0.460 |
|
| pre_ltx vs Groupnon-rPSC |
0.396 |
0.040 |
* |
| pre_ltx vs GrouprPSC |
0.293 |
0.243 |
|
Colon
results_model <- pairwise.lmer(
formula = "Shannon ~ Group + (1|Patient)",
factors=metadata_colon$Group,
data=metadata_colon %>% mutate(Patient=PatientID))
knitr::kable(results_model[[1]][,c("Estimate", "p.adj","sig")],
digits=3,caption="Results of linear modeling")
Results of linear modeling
| healthy vs Groupnon-rPSC |
-0.186 |
0.112 |
|
| healthy vs GrouprPSC |
-0.333 |
0.015 |
* |
| healthy vs Grouppre_ltx |
-0.585 |
0.000 |
*** |
| non-rPSC vs GrouprPSC |
-0.147 |
0.354 |
|
| pre_ltx vs Groupnon-rPSC |
0.397 |
0.039 |
* |
| pre_ltx vs GrouprPSC |
0.251 |
0.289 |
|
MDI and clinical variables
variables <- c("Bilirubin",
"log_Bilirubin",
"ALP","log_ALP",
"Fecal.calprotectin",
"log_Fecal.calprotectin",
"MAYO_PSC","AOM_score",
"APRI_score","log_APRI_score",
"FIB4_score","log_FIB4_score")
variables <- c("ALP","log_ALP",
"GGT","log_GGT",
"ALT","log_ALT",
"AST","log_AST",
"Bilirubin","log_Bilirubin",
"INR",
"Albumin",
"Platelets",
"Creatinine","log_Creatinine",
"FIB4_score",
"APRI_score",
"MAYO_PSC",
"AOM_score",
"Fecal.calprotectin",
"Nancy_max",
"eMAYO",
"MAYO_dai"
)
boxplots_plots <- list()
corrs <- list()
clinical_plots <- list()
for (clinical_variable in variables){
if (grepl("log_",clinical_variable)) {
metadata_for_boxplots[,clinical_variable] <- log(as.numeric(metadata_for_boxplots[,gsub("log_","",clinical_variable)]))
metadata_ileum[,clinical_variable] <- log(as.numeric(metadata_ileum[,gsub("log_","",clinical_variable)]))
metadata_colon[,clinical_variable] <- log(as.numeric(metadata_colon[,gsub("log_","",clinical_variable)]))
}
# boxplot
p <- clinical_boxplot(metadata_for_boxplots,
variable=clinical_variable)
boxplots_plots[[clinical_variable]] <- p
# ILEUM
# correlation
## ASV
level="ASV"
corr <- clinical_correlation(metadata_ileum,
clinical_variable,
level)
corrs[[paste0("ileum_asv_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
metadata_ileum,
clinical_variable,
level)
clinical_plots[[paste0("ileum_asv_",clinical_variable)]] <- p
## Genus
level="genus"
corr <- clinical_correlation(metadata_ileum,
clinical_variable,
level)
corrs[[paste0("ileum_genus_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
metadata_ileum,
clinical_variable,
level)
clinical_plots[[paste0("ileum_genus_",clinical_variable)]] <- p
# correlation
## ASV
level="ASV"
corr <- clinical_correlation(metadata_colon,
clinical_variable,
level,
segment="colon")
corrs[[paste0("colon_asv_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
metadata_colon,
clinical_variable,
level)
clinical_plots[[paste0("colon_asv_",clinical_variable)]] <- p
## Genus
level="genus"
corr <- clinical_correlation(metadata_colon,
clinical_variable,
level,
segment="colon")
corrs[[paste0("colon_genus_",clinical_variable)]] <- corr
p <- clinical_scatter(corr,
metadata_colon,
clinical_variable,
level)
clinical_plots[[paste0("colon_genus_",clinical_variable)]] <- p
}
variables <- c("Nancy_max","eMAYO","MAYO_dai")
for (clinical_variable in variables){
# CONTINGENCY TABLE
p <- clinical_contingency(metadata_for_boxplots,
clinical_variable)
boxplots_plots[[clinical_variable]] <- p
# ILEUM
## ASV
p <- clinical_boxplot_categorical(metadata_ileum,
clinical_variable,
level = "ASV")
clinical_plots[[paste0("ileum_asv_",clinical_variable)]] <- p
## GENUS
p <- clinical_boxplot_categorical(metadata_ileum,
clinical_variable,
level = "Genus")
clinical_plots[[paste0("ileum_genus_",clinical_variable)]] <- p
# COLON
## ASV
p <- clinical_boxplot_categorical(metadata_ileum,
clinical_variable,
level = "ASV")
clinical_plots[[paste0("colon_asv_",clinical_variable)]] <- p
## GENUS
p <- clinical_boxplot_categorical(metadata_ileum,
clinical_variable,
level = "Genus")
clinical_plots[[paste0("colon_genus_",clinical_variable)]] <- p
}
Boxplots
for (i in 1:length(boxplots_plots)){
name <- names(boxplots_plots)[i]
pl <- boxplots_plots[[i]]
pl <- pl + ggtitle(name)
boxplots_plots[[i]] <- pl
}
bx <- ggarrange(plotlist = boxplots_plots, ncol = 4,nrow=4)
bx
$`1`
$`2`
attr(,"class")
[1] "list" "ggarrange"


pdf("results/clinical/clinical_data_boxplots.pdf",height = 15,width =13)
bx
dev.off()
Clinical plots - MDI ~ clinical variable
for (i in 1:length(clinical_plots)){
name <- names(clinical_plots)[i]
pl <- clinical_plots[[i]]
pl <- pl + ggtitle(name)
clinical_plots[[i]] <- pl
}
cl_asv <- ggarrange(
plotlist = clinical_plots[grepl("asv",names(clinical_plots))],
ncol=4,nrow=8)
Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 161 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 341 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 161 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 341 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 331 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 331 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 332 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 332 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 122 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 244 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 153 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 326 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 340 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 150 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 319 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 330 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 20 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 36 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 20 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 34 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 199 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 208 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 208 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 208 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'
cl_genus <- ggarrange(plotlist = clinical_plots[grepl("genus",names(clinical_plots))],
ncol=4,nrow=8)
Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 161 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 341 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 161 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 341 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 331 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 331 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 332 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 332 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 122 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 244 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 153 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 326 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 340 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 162 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 342 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 150 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 319 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 156 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 330 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 20 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 36 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 20 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 34 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 199 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 208 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 208 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 103 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'Warning: All aesthetics have length 1, but the data has 208 rows.
ℹ Please consider using `annotate()` or provide this layer with data
containing a single row.`geom_smooth()` using formula = 'y ~ x'
cl_asv
$`1`
$`2`
attr(,"class")
[1] "list" "ggarrange"


cl_genus
$`1`
$`2`
attr(,"class")
[1] "list" "ggarrange"


pdf("results/clinical/correlation.pdf",height = 22,width =18)
cl_asv
dev.off()
pdf("results/clinical/correlation_genus.pdf",height = 22,width =18)
cl_genus
dev.off()
HEATMAP
ASV level
corrs_ileum <- corrs[grepl("ileum_asv",names(corrs))]
corrs_colon <- corrs[grepl("colon_asv",names(corrs))]
prepared_list <- prepare_for_heatmap(corrs_ileum,corrs_colon)
p_df_sig_mdi <- prepared_list[[1]]
r_df_mdi <- prepared_list[[2]]
r_df_melt_mdi <- melt(r_df_mdi %>% rownames_to_column("SeqID"))
Warning: The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(r_df_mdi %>% rownames_to_column("SeqID")). In the next version, this warning will become an error.Using SeqID as id variables
p_df_melt_mdi <- melt(p_df_sig_mdi %>% rownames_to_column("SeqID"),
id.vars = c("SeqID"))
Warning: The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(p_df_sig_mdi %>% rownames_to_column("SeqID")). In the next version, this warning will become an error.
r_df_melt_mdi$SeqID <- factor(r_df_melt_mdi$SeqID,
levels=unique(r_df_melt_mdi$SeqID))
p_df_melt_mdi$SeqID <- factor(p_df_melt_mdi$SeqID,
levels=unique(p_df_melt_mdi$SeqID))
r_df_melt_mdi$variable <- factor(r_df_melt_mdi$variable,
levels=rev(unique(r_df_melt_mdi$variable)))
p_df_melt_mdi$variable <- factor(p_df_melt_mdi$variable,
levels=rev(unique(p_df_melt_mdi$variable)))
p <- ggplot() +
geom_tile(data=r_df_melt_mdi, aes(SeqID, variable, fill= value)) +
theme_minimal() +
scale_fill_gradient2(name = "Rho", low = "blue", mid = "white", high = "red", midpoint = 0,limits = c(-0.7, 0.7)) +
geom_text(data=p_df_melt_mdi,aes(x=SeqID,y=variable,label=value,vjust=0.6)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab("") + ylab("") +
ggtitle("MDI")+
theme(plot.title = element_text(hjust=0.5,face = "bold",size = 15))
p

Genus level
corrs_ileum <- corrs[grepl("ileum_genus",names(corrs))]
corrs_colon <- corrs[grepl("colon_genus",names(corrs))]
prepared_list <- prepare_for_heatmap(corrs_ileum,corrs_colon)
p_df_sig_mdi <- prepared_list[[1]]
r_df_mdi <- prepared_list[[2]]

Bacteria and significant clinical variables
Terminal ileum
Genus level
level="genus"
Aggregation, filtering
# Aggregation
genus_data <- aggregate_taxa(ileum_asv_tab,
ileum_taxa_tab,
taxonomic_level=level,
names=TRUE)
# Filtration
filt_data <- filtering_steps(genus_data[[1]],
genus_data[[2]],
ileum_metadata,
seq_depth_threshold=10000)
Removing 3 ASV(s)
filt_ileum_genus_tab <- filt_data[[1]]
filt_ileum_genus_taxa <- filt_data[[2]]
filt_ileum_metadata_genus <- filt_data[[3]]
psc_effect_ileum_genus <- read.xlsx(
"../results/Q1_czech/univariate_analysis/supplements_psc_effect_terminal_ileum.xlsx",
sheet = "terminal_ileum genus")
psc_taxa <- psc_effect_ileum_genus$SeqID
increased <- psc_effect_ileum_genus$ASV[psc_effect_ileum_genus$log2FoldChange >0]
decreased <- psc_effect_ileum_genus$ASV[psc_effect_ileum_genus$log2FoldChange <0]
Increased + decreased
my_table <- filt_ileum_genus_tab %>% column_to_rownames("SeqID")
data_clr <- vegan::decostand(my_table,method = "clr", MARGIN = 2,pseudocount=0.5) %>% as.matrix()
data_clr <- data_clr[psc_taxa,] %>% t() %>% as.data.frame() %>% rownames_to_column("SampleID")
metadata_with_abundances <- data_clr %>% full_join(metadata_ileum,by="SampleID")
# significant variables
variables <- c("ALP",
"GGT",
"APRI_score",
"FIB4_score",
"Albumin",
"Platelets")
corrs_ileum <- corrs[grepl("ileum_genus",names(corrs))]
corrs_colon <- corrs[grepl("colon_genus",names(corrs))]
prepared_list <- prepare_for_heatmap(corrs_ileum,corrs_colon)
p_df_sig_mdi <- prepared_list[[1]]
variables <- colnames(p_df_sig_mdi)[!apply(p_df_sig_mdi,2,function(x) {
all(x == "")})]
corrs <- list()
clinical_plots <- list()
for (clinical_variable in variables){
for (taxon in psc_taxa){
if (grepl("log_",clinical_variable)) {
metadata_with_abundances[,clinical_variable] <- log(as.numeric(metadata_with_abundances[,gsub("log_","",clinical_variable)]))
}
# ILEUM
# correlation
corr <- clinical_correlation_abundances(metadata_with_abundances,clinical_variable,taxon,level="genus")
corrs[[paste0("ileum_genus_",clinical_variable,"_", taxon)]] <- corr
if (corr$P <0.05){
p <- clinical_scatter_abundances(corr,metadata_with_abundances,clinical_variable,taxon,level="genus",size = 3)
clinical_plots[[paste0("ileum_genus_",clinical_variable,"_",taxon)]] <- p
} else print(corr$P)
}
}
[1] 0.1795378
[1] 0.1546398
[1] 0.1545929
[1] 0.8736376
[1] 0.1211876
[1] 0.2656531
[1] 0.7206887
[1] 0.2736377
[1] 0.3640954
[1] 0.1392426
[1] 0.1462809
[1] 0.3744141
[1] 0.07350866
[1] 0.370275
[1] 0.05424225
[1] 0.1027328
[1] 0.06987345
[1] 0.05892518
[1] 0.06594912
[1] 0.2342706
[1] 0.1622938
[1] 0.8614502
[1] 0.0530168
[1] 0.3562072
[1] 0.9746608
[1] 0.559969
[1] 0.07848014
[1] 0.1025848
[1] 0.4607077
[1] 0.09592839
[1] 0.4488182
[1] 0.7472116
[1] 0.4482218
[1] 0.9986045
[1] 0.2183595
[1] 0.9121244
[1] 0.1448566
[1] 0.1691357
[1] 0.8578561
[1] 0.6487299
[1] 0.7833458
[1] 0.2263479
prepared_list <- subprepare_for_heatmap(corrs,MDI = FALSE)
p_df_sig <- prepared_list[[1]]
r_df <- prepared_list[[2]]
r_df_melt <- melt(r_df %>% rownames_to_column("SeqID"))
Warning: The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(r_df %>% rownames_to_column("SeqID")). In the next version, this warning will become an error.Using SeqID as id variables
p_df_melt <- melt(p_df_sig %>% rownames_to_column("SeqID"),id.vars = c("SeqID"))
Warning: The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(p_df_sig %>% rownames_to_column("SeqID")). In the next version, this warning will become an error.
r_df_melt$variable <- factor(r_df_melt$variable,
levels=rev(unique(r_df_melt$variable)))
p_df_melt$variable <- factor(p_df_melt$variable,
levels =rev(unique(p_df_melt$variable)))
r_df_melt$SeqID <- factor(r_df_melt$SeqID, levels = unique(r_df_melt$SeqID))
p_df_melt$SeqID <- factor(p_df_melt$SeqID, levels = unique(p_df_melt$SeqID))
p_ileum <- ggplot() +
geom_tile(data=r_df_melt, aes(variable, SeqID, fill= value)) +
theme_minimal() +
scale_fill_gradient2(name = "Rho", low = "blue", mid = "white", high = "red", midpoint = 0,limits = c(-0.5, 0.5)) +
geom_text(data=p_df_melt,aes(x=variable,y=SeqID,label=value,vjust=0.6)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab("") + ylab("") +
ggtitle("Terminal ileum")+
theme(plot.title = element_text(hjust=0.5,face = "bold",size = 15))
p_ileum

Colon
Genus level
Aggregation, filtering
genus_data <- aggregate_taxa(colon_asv_tab,
colon_taxa_tab,
taxonomic_level="Genus",
names=TRUE)
filt_colon_genus_tab <- genus_data[[1]]
filt_colon_genus_taxa <- genus_data[[2]]
filt_colon_metadata <- colon_metadata
psc_effect_colon_genus <- read.xlsx("../results/Q1_czech/univariate_analysis/supplements_psc_effect_colon.xlsx",
sheet = "colon genus")
psc_taxa <- psc_effect_colon_genus$SeqID
increased <- psc_effect_colon_genus$SeqID[psc_effect_colon_genus$log2FoldChange >0]
decreased <- psc_effect_colon_genus$SeqID[psc_effect_colon_genus$log2FoldChange <0]
my_table <- filt_colon_genus_tab %>% column_to_rownames("SeqID")
data_clr <- vegan::decostand(my_table,method = "clr", MARGIN = 2,pseudocount=0.5) %>% as.matrix()
data_clr <- data_clr[psc_taxa,] %>% t() %>% as.data.frame() %>% rownames_to_column("SampleID")
metadata_with_abundances <- data_clr %>% full_join(metadata_colon,by="SampleID")
variables <- c("ALP",
"GGT",
"APRI_score",
"FIB4_score",
"Albumin",
"Platelets")
corrs <- list()
clinical_plots <- list()
for (clinical_variable in variables){
for (taxon in psc_taxa){
if (grepl("log_",clinical_variable)) {
metadata_with_abundances[,clinical_variable] <- log(as.numeric(metadata_with_abundances[,gsub("log_","",clinical_variable)]))
}
# ILEUM
# correlation
corr <- clinical_correlation_abundances(metadata_with_abundances,clinical_variable,taxon,level="genus")
corrs[[paste0("colon_genus_",clinical_variable,"_", taxon)]] <- corr
if ( (corr$P == "< 0.05") | (corr$P == "< 0.01") | (corr$P == "< 0.01") ){
p <- clinical_scatter_abundances(corr,metadata_with_abundances,clinical_variable,taxon,level="genus",size = 3)
clinical_plots[[paste0("colon_genus_",clinical_variable,"_",taxon)]] <- p
} else print(corr$P)
}
}
[1] 8.902927e-05
[1] 0.0005647247
[1] 0.003039914
[1] 0.06501386
[1] 2.738443e-06
[1] 9.798259e-05
[1] 0.1554447
[1] 0.001503334
[1] 0.805154
[1] 0.04703198
[1] 0.1156528
[1] 0.1030576
[1] 0.04922751
[1] 0.009930211
[1] 0.003050694
[1] 0.6894357
[1] 0.002591426
[1] 3.612922e-07
[1] 0.1067446
[1] 0.002122119
[1] 4.605107e-06
[1] 0.0005938918
[1] 0.2693496
[1] 0.1005732
[1] 0.0004793599
[1] 0.005430916
[1] 0.019736
[1] 0.0006409276
[1] 0.0818511
[1] 0.004261477
[1] 0.001501122
[1] 0.06822636
[1] 0.05750884
[1] 1.710327e-05
[1] 0.009648598
[1] 0.1632994
[1] 0.0008728726
[1] 1.872338e-05
[1] 0.3059893
[1] 0.002394405
[1] 6.641559e-08
[1] 0.0001727573
[1] 1.253515e-05
[1] 2.530732e-06
[1] 1.319317e-06
[1] 0.0002584068
[1] 0.006789728
[1] 2.021499e-06
[1] 3.704793e-05
[1] 1.16368e-05
[1] 0.000531207
[1] 2.6664e-05
[1] 0.004563502
[1] 6.481947e-05
[1] 1.052578e-05
[1] 0.0001434732
[1] 4.345159e-11
[1] 3.559443e-11
[1] 0.002081744
[1] 3.959186e-05
[1] 0.2993047
[1] 0.0265046
[1] 0.005909994
[1] 0.0002104112
[1] 0.004395611
[1] 0.02421451
[1] 0.8308171
[1] 0.4389772
[1] 0.6828513
[1] 0.0002059141
[1] 0.8829335
[1] 0.4547569
[1] 0.07186342
[1] 0.01705701
[1] 0.03607561
[1] 0.0003387316
[1] 0.00196421
[1] 0.1842222
[1] 0.004339134
[1] 0.4173678
[1] 0.6115159
[1] 0.005511981
[1] 0.004008966
[1] 2.864847e-05
[1] 0.119322
[1] 0.1893823
[1] 0.008536685
[1] 0.7151612
[1] 0.03595876
[1] 0.002057776
[1] 0.2738009
[1] 0.8535685
[1] 0.9109102
[1] 0.06012322
[1] 0.6505823
[1] 0.7126095
[1] 0.4899684
[1] 0.4528184
[1] 0.09320952
[1] 0.5288051
[1] 0.007417647
[1] 4.493443e-05
[1] 0.0004702686
[1] 0.0002254489
[1] 0.0001007374
[1] 0.01128886
[1] 0.08889771
[1] 0.6197157
[1] 0.2613796
[1] 0.07858962
[1] 0.5299679
[1] 0.4290324
[1] 0.164709
[1] 0.0005174712
[1] 0.7602667
[1] 0.1543431
[1] 0.0007063679
[1] 0.04209605
[1] 0.02932357
[1] 0.1576378
clinical_plots_colon <- clinical_plots
corrs_colon <- corrs
prepared_list <- subprepare_for_heatmap(corrs,MDI = FALSE)
p_df_sig <- prepared_list[[1]]
r_df <- prepared_list[[2]]
r_df_melt <- melt(r_df %>% rownames_to_column("SeqID"))
Warning: The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(r_df %>% rownames_to_column("SeqID")). In the next version, this warning will become an error.Using SeqID as id variables
p_df_melt <- melt(p_df_sig %>% rownames_to_column("SeqID"),id.vars = c("SeqID"))
Warning: The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::melt(p_df_sig %>% rownames_to_column("SeqID")). In the next version, this warning will become an error.
r_df_melt$variable <- factor(r_df_melt$variable,
levels=rev(unique(r_df_melt$variable)))
p_df_melt$variable <- factor(p_df_melt$variable,
levels =rev(unique(p_df_melt$variable)))
r_df_melt$SeqID <- factor(r_df_melt$SeqID, levels = unique(r_df_melt$SeqID))
p_df_melt$SeqID <- factor(p_df_melt$SeqID, levels = unique(p_df_melt$SeqID))
p_colon <- ggplot() +
geom_tile(data=r_df_melt, aes(variable, SeqID, fill= value)) +
theme_minimal() +
scale_fill_gradient2(name = "Rho", low = "blue", mid = "white", high = "red", midpoint = 0,limits = c(-0.5, 0.5)) +
geom_text(data=p_df_melt,aes(x=variable,y=SeqID,label=value,vjust=0.6)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
xlab("") + ylab("") +
ggtitle("Colon")+
theme(plot.title = element_text(hjust=0.5,face = "bold",size = 15))
p_colon

heatmap plot

PCA and clinical variables fit
Terminal ileum
Analysis
segment="terminal_ileum"
Main analysis - Genus, Aitchison
Genus level, Aitchison distance
level="genus"
Aggregation, filtering
# Aggregation
genus_data <- aggregate_taxa(ileum_asv_tab,
ileum_taxa_tab,
taxonomic_level=level,
names=TRUE)
# Filtration
filt_data <- filtering_steps(genus_data[[1]],
genus_data[[2]],
ileum_metadata,
seq_depth_threshold=10000)
filt_ileum_genus_tab <- filt_data[[1]]
filt_ileum_genus_taxa <- filt_data[[2]]
filt_ileum_metadata_genus <- filt_data[[3]]
Plots
p <- pca_plot_custom(filt_ileum_genus_tab,
filt_ileum_genus_taxa,
filt_ileum_metadata_genus,
show_boxplots = TRUE,
variable = "Group", size=3,
show_legend=TRUE,
clinical = TRUE,
clinical_metadata = metadata_cz)
# see the results
p
Supplementary analysis
Genus level
level="genus"
Bray-Curtis
Plots
p <- pca_plot_custom(filt_ileum_genus_tab,
filt_ileum_genus_taxa,
filt_ileum_metadata_genus,
measure = "bray",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE,
clinical_metadata=metadata_cz)
# see the results
p
Jaccard
Plots
p <- pca_plot_custom(filt_ileum_genus_tab,
filt_ileum_genus_taxa,
filt_ileum_metadata_genus,
measure = "jaccard",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE,
clinical_metadata = metadata_cz)
# see the results
p
ASV level
level="ASV"
Aitchison
PCoA
p <- pca_plot_custom(filt_ileum_asv_tab,
filt_ileum_taxa_tab,
filt_ileum_metadata,
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE,
clinical_metadata = metadata_cz)
# see the results
p
Bray-Curtis
PCoA
p <- pca_plot_custom(filt_ileum_asv_tab,
filt_ileum_taxa_tab,
filt_ileum_metadata,
measure = "bray",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE,
clinical_metadata=metadata_cz)
# see the results
p
Jaccard
PCoA
p <- pca_plot_custom(filt_ileum_asv_tab,
filt_ileum_taxa_tab,
filt_ileum_metadata,
measure = "jaccard",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE,
clinical_metadata=metadata_cz)
# see the results
p
Colon
segment="colon"
Filtering
Rules: - prevalence > 5% (per group) - nearZeroVar with default
settings - sequencing depth > 5000 - taxonomic assignment at least
order
Sequencing depth
data_filt <- seq_depth_filtering(colon_asv_tab,
colon_taxa_tab,
colon_metadata,
seq_depth_threshold = 10000)
filt_colon_asv_tab <- data_filt[[1]]; alpha_colon_asv_tab <- filt_colon_asv_tab
filt_colon_taxa_tab <- data_filt[[2]]; alpha_colon_taxa_tab <- filt_colon_taxa_tab
filt_colon_metadata <- data_filt[[3]]; alpha_colon_metadata <- filt_colon_metadata
seq_step <- dim(filt_colon_asv_tab)[1]
NearZeroVar
data_filt <- nearzerovar_filtering(filt_colon_asv_tab,
filt_colon_taxa_tab,
filt_colon_metadata)
filt_colon_asv_tab <- data_filt[[1]]
filt_colon_taxa_tab <- data_filt[[2]]
nearzero_step <- dim(filt_colon_asv_tab)[1]
Check zero depth
data_filt <- check_zero_depth(filt_colon_asv_tab,
filt_colon_taxa_tab,
filt_colon_metadata)
filt_colon_asv_tab <- data_filt[[1]];
filt_colon_taxa_tab <- data_filt[[2]];
filt_colon_metadata <- data_filt[[3]];
Calculating Aitchison distance (euclidean distance on clr-transformed
data), both at ASV and genus level.
Main analysis - Genus, Aitchison
Genus level, Aitchison distance
level="genus"
Aggregation, filtering
# Aggregation
genus_data <- aggregate_taxa(colon_asv_tab,
colon_taxa_tab,
taxonomic_level=level,
names=TRUE)
# Filtration
filt_data <- filtering_steps(genus_data[[1]],
genus_data[[2]],
colon_metadata,
seq_depth_threshold=10000)
filt_colon_genus_tab <- filt_data[[1]]
filt_colon_genus_taxa <- filt_data[[2]]
filt_colon_genus_metadata <- filt_data[[3]]
PCoA
p <- pca_plot_custom(filt_colon_genus_tab,
filt_colon_genus_taxa,
filt_colon_genus_metadata,
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE, clinical_metadata = metadata_cz)
# see the results
p
Supplementary analysis
Genus level
level="genus"
Bray-Curtis
Plots
p <- pca_plot_custom(filt_colon_genus_tab,
filt_colon_genus_taxa,
filt_colon_genus_metadata,
measure = "bray",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE, clinical_metadata = metadata_cz)
# see the results
p
Jaccard
Plots
p <- pca_plot_custom(filt_colon_genus_tab,
filt_colon_genus_taxa,
filt_colon_genus_metadata,
measure = "jaccard",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE, clinical_metadata = metadata_cz)
# see the results
p
ASV level
level="ASV"
Aitchison
PCoA
p <- pca_plot_custom(filt_colon_asv_tab,
filt_colon_taxa_tab,
filt_colon_metadata,
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE, clinical_metadata = metadata_cz)
# see the results
p
Bray-Curtis
PCoA
p <- pca_plot_custom(filt_colon_asv_tab,
filt_colon_taxa_tab,
filt_colon_metadata,
measure = "bray",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE, clinical_metadata = metadata_cz)
# see the results
p
Jaccard
PCoA
p <- pca_plot_custom(filt_colon_asv_tab,
filt_colon_taxa_tab,
filt_colon_metadata,
measure = "jaccard",
show_boxplots = TRUE,
variable = "Group", size=3, show_legend=TRUE,
clinical=TRUE, clinical_metadata = metadata_cz)
# see the results
p
LS0tDQp0aXRsZTogIlBTQyBjbGluaWNhbCBpbXBhY3QiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmxpYnJhcnkob3Blbnhsc3gpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KHBpY2FudGUpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KG1pY2UpDQpzb3VyY2UoImN1c3RvbV9mdW5jdGlvbnMuUiIpDQpgYGANCg0KIyBGdW5jdGlvbnMNCg0KYGBge3J9DQpjbGluaWNhbF9ib3hwbG90IDwtIGZ1bmN0aW9uKG1ldGFkYXRhLHZhcmlhYmxlKXsNCiAgbWV0YWRhdGFfdmFyIDwtIG1ldGFkYXRhICU+JSBkcGx5cjo6c2VsZWN0KGFsbF9vZihjKCJQYXRpZW50SUQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLCAiR3JvdXAiKSkpICU+JSBhcy5kYXRhLmZyYW1lKCkNCiAgbWV0YWRhdGFfdmFyWyx2YXJpYWJsZV0gPC0gYXMubnVtZXJpYyhtZXRhZGF0YV92YXJbLHZhcmlhYmxlXSkNCiAgbWV0YWRhdGFfdmFyX3dpdGhvdXRfbmEgPC0gbWV0YWRhdGFfdmFyWyFpcy5uYShtZXRhZGF0YV92YXJbLHZhcmlhYmxlXSksXQ0KICBjb2xvcnMgPC0gYygiIzMwOWY4NyIsIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpDQogIGlmIChsZW5ndGgodW5pcXVlKG1ldGFkYXRhX3Zhcl93aXRob3V0X25hJEdyb3VwKSk9PTEpIGNvbG9ycyA8LSBjKCIjZjljNjc1IikNCiAgaWYgKCEiaGVhbHRoeSIgJWluJSB1bmlxdWUobWV0YWRhdGFfdmFyX3dpdGhvdXRfbmEkR3JvdXApKSBjb2xvcnMgPC0gYygiI2Y5YzY3NSIsIiNGMDgwODAiLCIjQTAwMDAwIikNCiAgDQpwIDwtIGdncGxvdChtZXRhZGF0YV92YXJfd2l0aG91dF9uYSkgKyANCiAgZ2VvbV9ib3hwbG90KGFlcyh4PUdyb3VwLCB5PSEhc3ltKHZhcmlhYmxlKSksb3V0bGllcnMgPSBGQUxTRSkgKyANCiAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMixoZWlnaHQgPSAwLGFlcyh4PUdyb3VwLCB5PSEhc3ltKHZhcmlhYmxlKSwgY29sb3I9R3JvdXApLHNpemU9MikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JzKSArIA0Kc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xvcnMpICsgDQogICB0aGVtZV9taW5pbWFsKCkgKyANCiB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAwKSkNCnJldHVybihwKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KY2xpbmljYWxfY29ycmVsYXRpb24gPC0gZnVuY3Rpb24obWV0YWRhdGEsdmFyaWFibGUsbGV2ZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50PSJpbGV1bSIpew0KICBzZXQuc2VlZCgxMjMpDQogIGxldmVsIDwtIHRvbG93ZXIobGV2ZWwpDQogIA0KICBtZXRhZGF0YV92YXIgPC0gbWV0YWRhdGEgJT4lIA0KICAgIGRwbHlyOjpzZWxlY3QoYWxsX29mKGMoIlNhbXBsZUlEIiwgIlBhdGllbnRJRCIsDQogICAgICAgICAgICAgICAgcGFzdGUwKCJkeXNfZmlsdGVyZWRfIixsZXZlbCksDQogICAgICAgICAgICAgICAgdmFyaWFibGUsICJHcm91cCIpKSkNCiAgDQogIG1ldGFkYXRhX3ZhclssdmFyaWFibGVdIDwtIGFzLm51bWVyaWMobWV0YWRhdGFfdmFyWyx2YXJpYWJsZV0pDQogIG1ldGFkYXRhX3ZhciA8LSBkcm9wX25hKG1ldGFkYXRhX3ZhcikNCiAgDQogIGlmIChzZWdtZW50PT0iY29sb24iKXsNCiAgICBuX2l0ZXJhdGlvbnMgPC0gMTAwDQogICAgY29ycnMgPC0gbGlzdCgpDQogICAgZm9yIChpIGluIDE6bl9pdGVyYXRpb25zKXsNCiAgICAgIHJhbmRvbV9zYW1wbGVkX2RmIDwtIG1ldGFkYXRhX3ZhciAlPiUNCiAgICAgIGdyb3VwX2J5KFBhdGllbnRJRCkgJT4lICAjIEdyb3VwIGJ5IFBhdGllbnRJRA0KICAgICAgc2xpY2Vfc2FtcGxlKG4gPSAxKSAlPiUgIyBSYW5kb21seSBwaWNrIG9uZSByb3cgcGVyIFBhdGllbnRJRA0KICAgICAgdW5ncm91cCgpDQogICAgICANCiAgICAgIGNvcnIgPC0gY29yLnRhYmxlKA0KICAgICAgICByYW5kb21fc2FtcGxlZF9kZlssYyh2YXJpYWJsZSxwYXN0ZTAoImR5c19maWx0ZXJlZF8iLGxldmVsKSldLCANCiAgICAgICAgY29yLm1ldGhvZD0ic3BlYXJtYW4iDQogICAgICAgICkNCiAgICAgIA0KICAgICAgY29yciRQIDwtIGNvcnIkUFsyXQ0KICAgICAgY29yciRyIDwtIGNvcnIkclsyXQ0KICAgICAgY29ycnNbW2ldXSA8LSBjb3JyDQogICAgfQ0KICAgIA0KICAgIHJfdmFsdWVzX2xpc3QgPC0gdW5saXN0KG1hcChjb3JycywgfiAueCRyKSkgICMgTGlzdCBvZiByIG1hdHJpY2VzDQogICAgcF92YWx1ZXNfbGlzdCA8LSB1bmxpc3QobWFwKGNvcnJzLCB+IC54JFApKSAgIyBMaXN0IG9mIFAgbWF0cmljZXMNCiAgICBtZWFuX3IgPC0gcm91bmQobWVkaWFuKHJfdmFsdWVzX2xpc3QpLDIpDQogICAgDQogICAgcF92YWx1ZSA8LSBxdWFudGlsZShwX3ZhbHVlc19saXN0LDAuOSkNCiAgICBjb3JyIDwtIGxpc3QoKQ0KICAgIGNvcnIkciA8LSBtZWFuX3INCiAgICBjb3JyJFAgPC0gcF92YWx1ZQ0KICB9IGVsc2Ugew0KICAgIGNvcnIgPC0gY29yLnRhYmxlKA0KICAgICAgbWV0YWRhdGFfdmFyWyxjKHZhcmlhYmxlLHBhc3RlMCgiZHlzX2ZpbHRlcmVkXyIsbGV2ZWwpKV0sDQogICAgICBjb3IubWV0aG9kPSJzcGVhcm1hbiIpDQogICAgDQogICAgY29yciRQIDwtIGNvcnIkUFsyXQ0KICAgIGNvcnIkciA8LSByb3VuZChjb3JyJHJbMl0sMikNCiAgfQ0KICANCiAgcmV0dXJuKGNvcnIpDQp9DQoNCmNsaW5pY2FsX2NvcnJlbGF0aW9uX2FidW5kYW5jZXMgPC0gZnVuY3Rpb24obWV0YWRhdGEsdmFyaWFibGUsdGF4b24sbGV2ZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQ9ImlsZXVtIil7DQogIHNldC5zZWVkKDEyMykNCiAgbGV2ZWwgPC0gdG9sb3dlcihsZXZlbCkNCiAgDQogIG1ldGFkYXRhX3ZhciA8LSBtZXRhZGF0YSAlPiUgZHBseXI6OnNlbGVjdChhbGxfb2YoYygiU2FtcGxlSUQiLCAiUGF0aWVudElEIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSx0YXhvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cCIpKSkNCiAgbWV0YWRhdGFfdmFyWyx2YXJpYWJsZV0gPC0gYXMubnVtZXJpYyhtZXRhZGF0YV92YXJbLHZhcmlhYmxlXSkNCiAgbWV0YWRhdGFfdmFyIDwtIGRyb3BfbmEobWV0YWRhdGFfdmFyKQ0KICANCiAgaWYgKHNlZ21lbnQ9PSJjb2xvbiIpew0KICAgIG5faXRlcmF0aW9ucyA8LSAxMDANCiAgICBjb3JycyA8LSBsaXN0KCkNCiAgICBmb3IgKGkgaW4gMTpuX2l0ZXJhdGlvbnMpew0KICAgICAgcmFuZG9tX3NhbXBsZWRfZGYgPC0gbWV0YWRhdGFfdmFyICU+JQ0KICAgICAgZ3JvdXBfYnkoUGF0aWVudElEKSAlPiUgICMgR3JvdXAgYnkgUGF0aWVudElEDQogICAgICBzbGljZV9zYW1wbGUobiA9IDEpICU+JSAjIFJhbmRvbWx5IHBpY2sgb25lIHJvdyBwZXIgUGF0aWVudElEDQogICAgICB1bmdyb3VwKCkNCiAgICAgIA0KICAgICAgY29yciA8LSBjb3IudGFibGUocmFuZG9tX3NhbXBsZWRfZGZbLGModmFyaWFibGUsdGF4b24pXSwgY29yLm1ldGhvZD0ic3BlYXJtYW4iKQ0KICAgICAgY29yciRQIDwtIGNvcnIkUFsyXQ0KICAgICAgY29yciRyIDwtIGNvcnIkclsyXQ0KICAgICAgY29ycnNbW2ldXSA8LSBjb3JyDQogICAgfQ0KICAgICByX3ZhbHVlc19saXN0IDwtIHVubGlzdChtYXAoY29ycnMsIH4gLngkcikpICAjIExpc3Qgb2YgciBtYXRyaWNlcw0KICAgIHBfdmFsdWVzX2xpc3QgPC0gdW5saXN0KG1hcChjb3JycywgfiAueCRQKSkgICMgTGlzdCBvZiBQIG1hdHJpY2VzDQogICAgbWVhbl9yIDwtIHJvdW5kKG1lZGlhbihyX3ZhbHVlc19saXN0KSwyKQ0KICAgIA0KICAgIHBfdmFsdWUgPC0gcXVhbnRpbGUocF92YWx1ZXNfbGlzdCwwLjkpDQogICAgI21pbl9wIDwtIG1pbihwX3ZhbHVlc19saXN0KQ0KICAgICNtaW5fcCA8LSByb3VuZChtaW5fcCwyKQ0KICAgICNtaW5fcCA8LSBpZmVsc2UobWluX3A8PTAuMDUsIjwgMC4wNSIsaWZlbHNlKG1pbl9wPD0wLjAxLCI8IDAuMDEiLGlmZWxzZShtaW5fcDw9MC4wMDEsIjwgMC4wMDEiLHBhc3RlMCgiPSAiLG1pbl9wKSkpKQ0KICAgIA0KICAgICNtYXhfcCA8LSBtYXgocF92YWx1ZXNfbGlzdCkNCiAgICAjbWF4X3AgPC0gcm91bmQobWF4X3AsMikNCiAgICAjbWF4X3AgPC0gaWZlbHNlKG1heF9wPD0wLjA1LCI8IDAuMDUiLGlmZWxzZShtYXhfcDw9MC4wMSwiPCAwLjAxIixpZmVsc2UobWF4X3A8PTAuMDAxLCI8IDAuMDAxIixtYXhfcCkpKQ0KICAgIA0KICAgICNpZiAobWluX3A9PW1heF9wKSBjb21iaW5lZF9kZiA8LSBtaW5fcA0KICAgICNlbHNlIHsNCiAgICAjICBtYXhfcCA8LSByb3VuZChxdWFudGlsZShjb3JyJFAsMC45KSwyKQ0KICAgICMgIGNvbWJpbmVkX2RmIDwtIGlmZWxzZShtYXhfcDw9MC4wNSwiPCAwLjA1IixpZmVsc2UobWF4X3A8PTAuMDEsIjwgMC4wMSIsaWZlbHNlKG1heF9wPD0wLjAwMSwiPCAwLjAwMSIscGFzdGUoIj0iLG1heF9wKSApKSkNCiAgICAjfQ0KICAgIGNvcnIgPC0gbGlzdCgpDQogICAgY29yciRyIDwtIG1lYW5fcg0KICAgICNjb3JyJFAgPC0gY29tYmluZWRfZGYNCiAgICBjb3JyJFAgPC0gcF92YWx1ZQ0KICB9IGVsc2Ugew0KICAgIGNvcnIgPC0gY29yLnRhYmxlKG1ldGFkYXRhX3ZhclssYyh2YXJpYWJsZSx0YXhvbildLCBjb3IubWV0aG9kPSJzcGVhcm1hbiIpDQogICAgI21heF9wIDwtIHJvdW5kKGNvcnIkUFsyXSwyKQ0KICAgICNtYXhfcCA8LSANCiAgICAjICBtYXhfcCA8LSBpZmVsc2UobWF4X3A8PTAuMDUsIjwgMC4wNSIsaWZlbHNlKG1heF9wPD0wLjAxLCI8IDAuMDEiLGlmZWxzZShtYXhfcDw9MC4wMDEsIjwgMC4wMDEiLHBhc3RlMCgiPSAiLG1heF9wKSkpKQ0KICAgICNjb3JyJFAgPC0gbWF4X3ANCiAgICBjb3JyJHIgPC0gcm91bmQoY29yciRyWzJdLDIpDQogICAgY29yciRQIDwtIGNvcnIkUFsyXQ0KICB9DQogIA0KICByZXR1cm4oY29ycikNCn0NCg0KY2xpbmljYWxfc2NhdHRlciA8LSBmdW5jdGlvbihjb3JyLG1ldGFkYXRhLHZhcmlhYmxlLGxldmVsKXsNCiAgbGV2ZWwgPC0gdG9sb3dlcihsZXZlbCkNCiAgZGZfZm9yX3NjYXR0ZXJfcGxvdCA8LSBtZXRhZGF0YSAlPiUgDQogICAgZHBseXI6OnNlbGVjdChhbGxfb2YoYygiU2FtcGxlSUQiLCAiUGF0aWVudElEIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiZHlzX2ZpbHRlcmVkXyIsbGV2ZWwpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUsICJHcm91cCIpKSkNCiAgDQogIGRmX2Zvcl9zY2F0dGVyX3Bsb3RbLHZhcmlhYmxlXSA8LSBhcy5udW1lcmljKGRmX2Zvcl9zY2F0dGVyX3Bsb3RbLHZhcmlhYmxlXSkNCiAgDQogIGRmX2Zvcl9zY2F0dGVyX3Bsb3QgPC0gZHJvcF9uYShkZl9mb3Jfc2NhdHRlcl9wbG90KQ0KICANCiAgY29sb3JzIDwtIGMoIiMzMDlmODciLCIjZjljNjc1IiwiI0YwODA4MCIsIiNBMDAwMDAiKQ0KICBpZiAoISJoZWFsdGh5IiAlaW4lIHVuaXF1ZShkZl9mb3Jfc2NhdHRlcl9wbG90JEdyb3VwKSkgY29sb3JzIDwtIGMoIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpDQogIA0KICByX2NvcnIgPC0gY29yciRyDQogIHBfY29yciA8LSBjb3JyJFANCiAgcF9jb3JyIDwtIGlmZWxzZShwX2NvcnIgPCAwLjAwMSwgIjwwLjAwMSIsIGlmZWxzZShwX2NvcnIgPCAwLjAxLCAiPDAuMDEiLCBpZmVsc2UocF9jb3JyIDwgMC4wNSwgIjwwLjA1IiwgcGFzdGUwKCI9Iixyb3VuZChwX2NvcnIsMykpKSkpDQogIA0KICB4X3ZhciA8LSBwYXN0ZTAoImR5c19maWx0ZXJlZF8iLGxldmVsKQ0KICB4X3Bvc2l0aW9uIDwtIChtYXgoZGZfZm9yX3NjYXR0ZXJfcGxvdFsseF92YXJdKSkgLSAwLjIqKG1heChkZl9mb3Jfc2NhdHRlcl9wbG90Wyx4X3Zhcl0pIC0gbWluKGRmX2Zvcl9zY2F0dGVyX3Bsb3RbLHhfdmFyXSkpDQogIA0KICB5X3Bvc2l0aW9uIDwtIG1heChkZl9mb3Jfc2NhdHRlcl9wbG90Wyx2YXJpYWJsZV0pIC0gMC4wNSoobWF4KGRmX2Zvcl9zY2F0dGVyX3Bsb3RbLHZhcmlhYmxlXSkgLSBtaW4oZGZfZm9yX3NjYXR0ZXJfcGxvdFssdmFyaWFibGVdKSkNCiAgDQogIGxhYmVscyA8LSBwYXN0ZTAoInIgPSAiLHJfY29yciwgIlxcbnAgIixwX2NvcnIpDQogIHAgPC0gZ2dwbG90KGRmX2Zvcl9zY2F0dGVyX3Bsb3QpICsgDQogIGdlb21fcG9pbnQoYWVzKHg9ISFzeW0oeF92YXIpLCB5PSEhc3ltKHZhcmlhYmxlKSwgY29sb3I9R3JvdXApKSArIA0KICBnZW9tX3Ntb290aChhZXMoeD0hIXN5bSh4X3ZhciksIHk9ISFzeW0odmFyaWFibGUpKSxtZXRob2Q9bG0sIHNlPVRSVUUpICsNCiAgICBnZW9tX3RleHQoYWVzKHg9eF9wb3NpdGlvbiwgeT15X3Bvc2l0aW9uKSwNCiAgICAgICAgICAgICAgICAgIGxhYmVsPWdzdWIoIlxcXFxuIiwgIlxuIiwgbGFiZWxzKSxoanVzdCA9IDApICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sb3JzKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAwKSkgKyANCiAgeGxhYigiTURJIikNCiAgcmV0dXJuKHApDQp9DQoNCmNsaW5pY2FsX3NjYXR0ZXJfYWJ1bmRhbmNlcyA8LSBmdW5jdGlvbihjb3JyLG1ldGFkYXRhLHZhcmlhYmxlLHRheG9uLGxldmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9NCl7DQogIGxldmVsIDwtIHRvbG93ZXIobGV2ZWwpDQogIGRmX2Zvcl9zY2F0dGVyX3Bsb3QgPC0gbWV0YWRhdGEgJT4lIGRwbHlyOjpzZWxlY3QoYWxsX29mKGMoIlNhbXBsZUlEIiwgIlBhdGllbnRJRCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4b24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUsICJHcm91cCIpKSkNCiAgZGZfZm9yX3NjYXR0ZXJfcGxvdFssdmFyaWFibGVdIDwtIGFzLm51bWVyaWMoZGZfZm9yX3NjYXR0ZXJfcGxvdFssdmFyaWFibGVdKQ0KICBkZl9mb3Jfc2NhdHRlcl9wbG90IDwtIGRyb3BfbmEoZGZfZm9yX3NjYXR0ZXJfcGxvdCkNCiAgY29sb3JzIDwtIGMoIiMzMDlmODciLCIjZjljNjc1IiwiI0YwODA4MCIsIiNBMDAwMDAiKQ0KICBpZiAoISJoZWFsdGh5IiAlaW4lIHVuaXF1ZShkZl9mb3Jfc2NhdHRlcl9wbG90JEdyb3VwKSkgY29sb3JzIDwtIGMoIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpDQogIA0KICB4X3ZhciA8LSB0YXhvbg0KICByX2NvcnIgPC0gY29yciRyDQogIHBfY29yciA8LSBjb3JyJFANCiAgDQogIHhfcG9zaXRpb24gPC0gKG1heChkZl9mb3Jfc2NhdHRlcl9wbG90Wyx4X3Zhcl0pKSAtIDAuMioobWF4KGRmX2Zvcl9zY2F0dGVyX3Bsb3RbLHhfdmFyXSkgLSBtaW4oZGZfZm9yX3NjYXR0ZXJfcGxvdFsseF92YXJdKSkNCiAgeV9wb3NpdGlvbiA8LSBtYXgoZGZfZm9yX3NjYXR0ZXJfcGxvdFssdmFyaWFibGVdKSAtIDAuMDUqKG1heChkZl9mb3Jfc2NhdHRlcl9wbG90Wyx2YXJpYWJsZV0pIC0gbWluKGRmX2Zvcl9zY2F0dGVyX3Bsb3RbLHZhcmlhYmxlXSkpDQogIGxhYmVscyA8LSBwYXN0ZTAoInIgPSAiLHJfY29yciwgIlxcbnAgIixwX2NvcnIpDQogIHAgPC0gZ2dwbG90KGRmX2Zvcl9zY2F0dGVyX3Bsb3QpICsgDQogIGdlb21fcG9pbnQoYWVzKHg9ISFzeW0oeF92YXIpLCB5PSEhc3ltKHZhcmlhYmxlKSwgY29sb3I9R3JvdXApKSArIA0KICBnZW9tX3Ntb290aChhZXMoeD0hIXN5bSh4X3ZhciksIHk9ISFzeW0odmFyaWFibGUpKSxtZXRob2Q9bG0sIHNlPVRSVUUpICsNCiAgICBnZW9tX3RleHQoYWVzKHg9eF9wb3NpdGlvbiwgeT15X3Bvc2l0aW9uLGxhYmVsPWdzdWIoIlxcXFxuIiwgIlxuIiwgbGFiZWxzKSksc2l6ZT1zaXplKSArIA0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbG9ycykgKyANCiAgdGhlbWVfbWluaW1hbCgpICsgDQogdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gMCkpDQogIHJldHVybihwKQ0KfQ0KDQpgYGANCg0KYGBge3J9DQpjbGluaWNhbF9jb250aW5nZW5jeSA8LSBmdW5jdGlvbihtZXRhZGF0YSx2YXJpYWJsZSl7DQogIG1ldGFkYXRhX3ZhciA8LSBtZXRhZGF0YSAlPiUgZHBseXI6OnNlbGVjdChhbGxfb2YoYygiUGF0aWVudElEIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSwgIkdyb3VwIikpKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQogIG1ldGFkYXRhX3Zhcl93aXRob3V0X25hIDwtIG1ldGFkYXRhX3ZhclshaXMubmEobWV0YWRhdGFfdmFyWyx2YXJpYWJsZV0pLF0NCiAgY29udF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKG1ldGFkYXRhX3Zhcl93aXRob3V0X25hJEdyb3VwLCBtZXRhZGF0YV92YXJfd2l0aG91dF9uYVssdmFyaWFibGVdKSkNCiAgDQogIHAgPC0gZ2dwbG90KGNvbnRfdGFibGUsIGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSBGcmVxKSkgKw0KICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiKSArICAjIFRpbGUgYmFja2dyb3VuZCBjb2xvcg0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDYpICsgICMgQWRkIG51bWJlcnMNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gImJsdWUiKSArICAjIENvbG9yIGdyYWRpZW50DQogIGxhYnModGl0bGUgPSAiQ29udGluZ2VuY3kgVGFibGUgd2l0aCBDb3VudHMiLCANCiAgICAgICB4ID0gIkdyb3VwIiwgeSA9IHZhcmlhYmxlKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQogIHJldHVybihwKQ0KfQ0KDQpjbGluaWNhbF9ib3hwbG90X2NhdGVnb3JpY2FsIDwtIGZ1bmN0aW9uKG1ldGFkYXRhLCB2YXJpYWJsZSxsZXZlbCl7DQogIGxldmVsPXRvbG93ZXIobGV2ZWwpDQogIG1ldGFkYXRhX3ZhciA8LSBtZXRhZGF0YSAlPiUgZHBseXI6OnNlbGVjdChhbGxfb2YoYygiU2FtcGxlSUQiLCAiUGF0aWVudElEIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImR5c19maWx0ZXJlZF8iLGxldmVsKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSwgIkdyb3VwIikpKQ0KICBtZXRhZGF0YV92YXIgPC0gZHJvcF9uYShtZXRhZGF0YV92YXIpDQogIG1ldGFkYXRhX3ZhclssdmFyaWFibGVdIDwtIGFzLmZhY3RvcihtZXRhZGF0YV92YXJbLHZhcmlhYmxlXSkNCg0KICB5X3ZhciA8LSBwYXN0ZTAoImR5c19maWx0ZXJlZF8iLGxldmVsKQ0KICBwIDwtIGdncGxvdChtZXRhZGF0YV92YXIpICsgDQogICAgZ2VvbV9ib3hwbG90KGFlcyh4PSEhc3ltKHZhcmlhYmxlKSwgeT0hIXN5bSh5X3ZhcikpLG91dGxpZXJzID0gRkFMU0UpICsgDQogICAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMixoZWlnaHQgPSAwLGFlcyh4PSEhc3ltKHZhcmlhYmxlKSwgeT0hIXN5bSh5X3ZhciksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1Hcm91cCksc2l6ZT0yKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpKSArIA0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpKSArIA0KICAgIHRoZW1lX21pbmltYWwoKSArIA0KIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJibGFjayIsIGZpbGwgPSBOQSwgc2l6ZSA9IDApKQ0KICByZXR1cm4ocCkNCnANCn0NCmBgYA0KDQpgYGB7cn0NCnN1YnByZXBhcmVfZm9yX2hlYXRtYXAgPC0gZnVuY3Rpb24oY29ycnNfc2VnbWVudCxNREk9VFJVRSl7DQogICAgY29ycnNfc2VnbWVudCA8LSBjb3Jyc19zZWdtZW50WyFncmVwbCgiX2xvZ18iLG5hbWVzKGNvcnJzX3NlZ21lbnQpKV0NCiAgICBuYW1lcyhjb3Jyc19zZWdtZW50KSA8LSBnc3ViKCJzY29yZV8iLCIiLG5hbWVzKGNvcnJzX3NlZ21lbnQpKQ0KICAgICMgRXhhbXBsZSBsaXN0IHN0cnVjdHVyZSBmb3IgZGVtb25zdHJhdGlvbg0KICAgICMgSW5pdGlhbGl6ZSBlbXB0eSBsaXN0cyBmb3IgciBhbmQgUCB2YWx1ZXMNCiAgICByX3ZhbHVlcyA8LSBsaXN0KCkNCiAgICBwX3ZhbHVlcyA8LSBsaXN0KCkNCiAgICANCiAgICAjIEV4dHJhY3Qgc2VnbWVudCBhbmQgY2xpbmljYWwgdmFyaWFibGVzDQogICAgZm9yIChuYW1lIGluIG5hbWVzKGNvcnJzX3NlZ21lbnQpKSB7DQogICAgICBpZiAoTURJKSB7DQogICAgICAgIGNsaW5pY2FsX3ZhciA8LSBzdWIoIl4oLio/Xyl7Mn0iLCAiIiwgIG5hbWUpDQogICAgICAgIGlmIChhbGwoZ3JlcGwoImlsZXVtIixuYW1lcyhjb3Jyc19zZWdtZW50KSkpKSB7DQogICAgICAgIHNlZ21lbnQgPC0gIk1ESSB0ZXJtaW5hbCBpbGV1bSINCiAgICAgIH0gZWxzZSBpZiAoYWxsKGdyZXBsKCJjb2xvbiIsbmFtZXMoY29ycnNfc2VnbWVudCkpKSkgew0KICAgICAgICBzZWdtZW50IDwtICJNREkgY29sb24iDQogICAgICB9IGVsc2UgKG1lc3NhZ2UoIlBST0JMRU0gd2l0aCBzZWdtZW50IikpDQogICAgICAgIA0KICAgICAgfQ0KICAgICAgZWxzZSB7DQogICAgICAgIHNlZ21lbnQgPC0gc3ViKCJeKC4qP18pezN9IiwgIiIsICBuYW1lKQ0KICAgICAgICBjbGluaWNhbF92YXIgPC0gc3ViKHNlZ21lbnQsIiIsIHN1YigiXiguKj9fKXsyfSIsICIiLCAgbmFtZSkpDQogICAgICAgIGNsaW5pY2FsX3ZhciA8LSBnc3ViKCJfIiwiIixjbGluaWNhbF92YXIpDQogICAgICB9DQogIA0KDQogICAgICAjIEZpbGwgdGhlIHIgYW5kIFAgbGlzdHMNCiAgICAgIGlmICghaXMubnVsbChjb3Jyc19zZWdtZW50W1tuYW1lXV0kcikpIHsNCiAgICAgICAgcl92YWx1ZXNbW3NlZ21lbnRdXVtjbGluaWNhbF92YXJdIDwtIGNvcnJzX3NlZ21lbnRbW25hbWVdXSRyDQogICAgICB9DQogICAgICANCiAgICAgIGlmICghaXMubnVsbChjb3Jyc19zZWdtZW50W1tuYW1lXV0kUCkpIHsNCiAgICAgICAgcF92YWx1ZXNbW3NlZ21lbnRdXVtjbGluaWNhbF92YXJdIDwtIGNvcnJzX3NlZ21lbnRbW25hbWVdXSRQDQogICAgICB9DQogICAgfQ0KDQogICAgIyBDb252ZXJ0IHRoZSBsaXN0cyB0byBkYXRhIGZyYW1lcw0KICAgIHJfZGYgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KHJfdmFsdWVzLCBmdW5jdGlvbih4KSBzZXROYW1lcyhhcy5kYXRhLmZyYW1lKHQoeCkpLCBuYW1lcyh4KSkpKQ0KICAgIHBfZGYgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KHBfdmFsdWVzLCBmdW5jdGlvbih4KSBzZXROYW1lcyhhcy5kYXRhLmZyYW1lKHQoeCkpLCBuYW1lcyh4KSkpKQ0KICAgIA0KICAgICMgQWRkIHJvdyBuYW1lcyBhcyBzZWdtZW50IG5hbWVzDQogICAgcm93bmFtZXMocl9kZikgPC0gbmFtZXMocl92YWx1ZXMpDQogICAgcm93bmFtZXMocF9kZikgPC0gbmFtZXMocF92YWx1ZXMpDQogICAgDQogICAgaWYgKE1ESSkgcGRfZGZfY29ycmVjdGVkIDwtIGFzLmRhdGEuZnJhbWUocC5hZGp1c3QocF9kZixtZXRob2Q9IkJIIikpDQogICAgZWxzZSBwZF9kZl9jb3JyZWN0ZWQgPC0gYXMuZGF0YS5mcmFtZShhcHBseShwX2RmLDIsZnVuY3Rpb24oeCkgcC5hZGp1c3QoeCwgbWV0aG9kPSJCSCIpKSkNCiAgICBwX2RmX3NpZyA8LSBwZF9kZl9jb3JyZWN0ZWQNCiAgICBwX2RmX3NpZ1ssXSA8LSAiIg0KICAgIA0KICAgIHBfZGZfc2lnW3BkX2RmX2NvcnJlY3RlZCA8IDAuMDVdIDwtICIqIg0KICAgIHBfZGZfc2lnW3BkX2RmX2NvcnJlY3RlZCA8IDAuMDFdIDwtICIqKiINCiAgICBwX2RmX3NpZ1twZF9kZl9jb3JyZWN0ZWQgPCAwLjAwMV0gPC0gIioqKiINCiAgICANCiAgICBpZiAoTURJKSBwX2RmX3NpZyA8LSB0KHBfZGZfc2lnKSAlPiUgDQogICAgICBhcy5kYXRhLmZyYW1lKCkgICU+JSANCiAgICAgIGByb3duYW1lczwtYChzZWdtZW50KQ0KICAgIA0KICAgIHJldHVybihsaXN0KHBfZGZfc2lnLHJfZGYpKQ0KfQ0KDQpwcmVwYXJlX2Zvcl9oZWF0bWFwIDwtIGZ1bmN0aW9uKGNvcnJzX2lsZXVtLGNvcnJzX2NvbG9uKXsNCg0KICBpbGV1bV9saXN0IDwtIHN1YnByZXBhcmVfZm9yX2hlYXRtYXAoY29ycnNfaWxldW0pDQogIGNvbG9uX2xpc3QgPC0gc3VicHJlcGFyZV9mb3JfaGVhdG1hcChjb3Jyc19jb2xvbikNCg0KICBwX2RmX3NpZyA8LSByYmluZChpbGV1bV9saXN0W1sxXV0sY29sb25fbGlzdFtbMV1dKQ0KICByX2RmIDwtIHJiaW5kKGlsZXVtX2xpc3RbWzJdXSxjb2xvbl9saXN0W1syXV0pDQoNCiAgcmV0dXJuKGxpc3QocF9kZl9zaWcscl9kZikpDQogIA0KfQ0KDQpgYGANCg0KDQoNCiMgSW1wb3J0IGNsaW5pY2FsIGRhdGENCg0KYGBge3J9DQojIGNsaW5pY2FsIG1ldGFkYXRhDQptZXRhZGF0YV9jbGluaWNhbCA8LSByZWFkLmNzdigiLi4vLi4vZGF0YS9jbGluaWNhbF9kYXRhL2NsaW5pY2FsX21ldGFkYXRhX2N6LmNzdiIpDQptZXRhZGF0YV9jbGluaWNhbCRQYXRpZW50SUQgPC0gYXMuY2hhcmFjdGVyKG1ldGFkYXRhX2NsaW5pY2FsJFBhdGllbnRJRCkNCg0KIyBEWVNCSU9TSVMNCm1ldGFkYXRhX2R5c2Jpb3NpcyA8LSByZWFkLmNzdigiLi4vLi4vZGF0YS9jbGluaWNhbF9kYXRhL2R5c2Jpb3Npc19tZXRhZGF0YS5jc3YiKSAlPiUNCiAgZHBseXI6OmZpbHRlcihDb3VudHJ5PT0iQ1oiKQ0KDQojIEFMUEhBIERJVkVSU0lUWQ0KbWV0YWRhdGFfYWxwaGFfaWxldW0gPC0gcmVhZC5jc3YoDQogICIuLi9yZXN1bHRzL1ExL2FscGhhX2RpdmVyc2l0eS9hbHBoYV9pbmRpY2VzX3Rlcm1pbmFsX2lsZXVtLmNzdiIpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKENvdW50cnk9PSJDWiIpDQoNCm1ldGFkYXRhX2FscGhhX2NvbG9uIDwtIHJlYWQuY3N2KA0KICAiLi4vcmVzdWx0cy9RMS9hbHBoYV9kaXZlcnNpdHkvYWxwaGFfaW5kaWNlc19jb2xvbi5jc3YiKSAlPiUNCiAgZHBseXI6OmZpbHRlcihDb3VudHJ5PT0iQ1oiKQ0KDQptZXRhZGF0YV9hbHBoYSA8LSByYmluZChtZXRhZGF0YV9hbHBoYV9pbGV1bSxtZXRhZGF0YV9hbHBoYV9jb2xvbikgJT4lDQogIG11dGF0ZShQYXRpZW50SUQ9UGF0aWVudCkgJT4lDQogIGRwbHlyOjpzZWxlY3QoLWMoUGF0aWVudCwgR3JvdXApKQ0KDQojIE1FUkdJTkcNCm1ldGFkYXRhX2N6IDwtIGZ1bGxfam9pbihtZXRhZGF0YV9jbGluaWNhbCwgbWV0YWRhdGFfZHlzYmlvc2lzLCBieT1jKCJTYW1wbGVJRCIsIk1hdHJpeCIsIlBhdGllbnRJRCIsIkdyb3VwIiwiQ291bnRyeSIpKQ0KDQptZXRhZGF0YV9jeiA8LSBmdWxsX2pvaW4obWV0YWRhdGFfY3osIG1ldGFkYXRhX2FscGhhLCBieT1jKCJTYW1wbGVJRCIsIlBhdGllbnRJRCIsIkNvdW50cnkiKSkNCg0KbWV0YWRhdGFfY3okR3JvdXAgPC0gZmFjdG9yKG1ldGFkYXRhX2N6JEdyb3VwLGxldmVscyA9IGMoImhlYWx0aHkiLCJwcmVfbHR4Iiwibm9uLXJQU0MiLCJyUFNDIikpDQpgYGANCg0KYGBge3J9DQp2YXJpYWJsZXMgPC0gYygiUGF0aWVudElEIiwgIkdyb3VwIiwNCiAgICAgICAgICAgICAgICJCaWxpcnViaW4iLCAiQWxidW1pbiIsDQogICAgICAgICAgICAgICAiQUxQIiwgIlBsYXRlbGV0cyIsDQogICAgICAgICAgICAgICAiRmVjYWwuY2FscHJvdGVjdGluIiwNCiAgICAgICAgICAgICAgICJNQVlPX1BTQyIsIkFPTV9zY29yZSIsIA0KICAgICAgICAgICAgICAgIkFQUklfc2NvcmUiLA0KICAgICAgICAgICAgICAgIkZJQjRfc2NvcmUiLA0KICAgICAgICAgICAgICAgIk5hbmN5X21heCIsImVNQVlPIiwiTUFZT19kYWkiLA0KICAgICAgICAgICAgICAgICAgICAgIkNyZWF0aW5pbmUiLA0KICAgICAgICAgICAgICAgICAgICAgIk5hIiwNCiAgICAgICAgICAgICAgICAgICAgICJBTFQiLA0KICAgICAgICAgICAgICAgICAgICAgIkFTVCIsDQogICAgICAgICAgICAgICAgICAgICAiR0dUIiwgIklOUiIpDQpgYGANCg0KYGBge3J9DQptZXRhZGF0YV9jeiRGZWNhbC5jYWxwcm90ZWN0aW5bbWV0YWRhdGFfY3okR3JvdXA9PSJoZWFsdGh5Il0gPC0gTkENCm1ldGFkYXRhX2N6JElOUlttZXRhZGF0YV9jeiRHcm91cD09ImhlYWx0aHkiXSA8LSBOQQ0KbWV0YWRhdGFfY3okRmVjYWwuY2FscHJvdGVjdGluW21ldGFkYXRhX2N6JEZlY2FsLmNhbHByb3RlY3Rpbj09Ij42MDAwIl0gPC0gNjAwMA0KbWV0YWRhdGFfY3okRmVjYWwuY2FscHJvdGVjdGluIDwtIGFzLm51bWVyaWMobWV0YWRhdGFfY3okRmVjYWwuY2FscHJvdGVjdGluKQ0KICANCm1ldGFkYXRhX2lsZXVtIDwtIG1ldGFkYXRhX2N6ICU+JSBzdWJzZXQoTWF0cml4PT0iVEkiKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQptZXRhZGF0YV9jb2xvbiA8LSBtZXRhZGF0YV9jeiAlPiUgc3Vic2V0KE1hdHJpeCE9IlRJIikgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KDQojIFRPIERPISEhISEhISEhISEhISEgbmVmdW5ndWplIGFrbyBtYQ0KbWV0YWRhdGFfZm9yX2JveHBsb3RzIDwtIG1ldGFkYXRhX2N6Wyx2YXJpYWJsZXNdICU+JQ0KICBncm91cF9ieShQYXRpZW50SUQpICU+JQ0KICBkaXN0aW5jdChQYXRpZW50SUQsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkNCg0KYGBgDQoNCiMgSW1wb3J0IG1pY3JvYmlvbWUgZGF0YQ0KDQoqKkRhdGEgSW1wb3J0KioNCg0KSW1wb3J0aW5nIEFTViwgdGF4YSBhbmQgbWV0YWRhdGEgdGFibGVzIGZvciBib3RoIEN6ZWNoIGFuZCBOb3J3YXkNCnNhbXBsZXMuDQoNCipDemVjaCoNCg0KYGBge3J9DQpwYXRoID0gIi4uLy4uL2RhdGEvYW5hbHlzaXNfcmVhZHlfZGF0YS9pa2VtLyINCmFzdl90YWJfaWtlbSA8LSBhcy5kYXRhLmZyYW1lKGZyZWFkKGZpbGUucGF0aChwYXRoLCJhc3ZfdGFibGVfaWtlbS5jc3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UpKQ0KdGF4YV90YWJfaWtlbSA8LSBhcy5kYXRhLmZyYW1lKGZyZWFkKGZpbGUucGF0aChwYXRoLCJ0YXhhX3RhYmxlX2lrZW0uY3N2IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSkpDQptZXRhZGF0YV9pa2VtIDwtIGFzLmRhdGEuZnJhbWUoZnJlYWQoZmlsZS5wYXRoKHBhdGgsIm1ldGFkYXRhX2lrZW0uY3N2IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSkpDQpgYGANCg0KKipTcGxpdGluZyB0byBzZWdtZW50cyoqDQoNCipUZXJtaW5hbCBpbGV1bSoNCg0KYGBge3J9DQppbGV1bV9kYXRhIDwtIG1lcmdpbmdfZGF0YShhc3ZfdGFiXzE9YXN2X3RhYl9pa2VtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYXN2X3RhYl8yPU5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0YXhhX3RhYl8xPXRheGFfdGFiX2lrZW0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0YXhhX3RhYl8yPU5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YV8xPW1ldGFkYXRhX2lrZW0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YV8yPU5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50PSJUSSIsUT0iY2xpbmljYWwiKQ0KDQppbGV1bV9hc3ZfdGFiIDwtIGlsZXVtX2RhdGFbWzFdXQ0KaWxldW1fdGF4YV90YWIgPC0gaWxldW1fZGF0YVtbMl1dDQppbGV1bV9tZXRhZGF0YSA8LSBpbGV1bV9kYXRhW1szXV0NCmBgYA0KDQoqQ29sb24qDQoNCmBgYHtyfQ0KY29sb25fZGF0YSA8LSBtZXJnaW5nX2RhdGEoYXN2X3RhYl8xPWFzdl90YWJfaWtlbSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzdl90YWJfMj1OVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4YV90YWJfMT10YXhhX3RhYl9pa2VtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4YV90YWJfMj1OVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGFfMT1tZXRhZGF0YV9pa2VtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGFfMj1OVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudD0iY29sb24iLFE9ImNsaW5pY2FsIikNCg0KY29sb25fYXN2X3RhYiA8LSBjb2xvbl9kYXRhW1sxXV0NCmNvbG9uX3RheGFfdGFiIDwtIGNvbG9uX2RhdGFbWzJdXQ0KY29sb25fbWV0YWRhdGEgPC0gY29sb25fZGF0YVtbM11dDQpgYGANCg0KKipGaWx0ZXJpbmcqKg0KDQpSdWxlczogLSBwcmV2YWxlbmNlIFw+IDUlIChwZXIgZ3JvdXApIC0gbmVhclplcm9WYXIgd2l0aCBkZWZhdWx0DQpzZXR0aW5ncyAtIHNlcXVlbmNpbmcgZGVwdGggXD4gNTAwMCAtIHRheG9ub21pYyBhc3NpZ25tZW50IGF0IGxlYXN0DQpvcmRlcg0KDQoqU2VxdWVuY2luZyBkZXB0aCoNCg0KYGBge3J9DQpkYXRhX2ZpbHQgPC0gc2VxX2RlcHRoX2ZpbHRlcmluZyhpbGV1bV9hc3ZfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWxldW1fdGF4YV90YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbGV1bV9tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcV9kZXB0aF90aHJlc2hvbGQgPSAxMDAwMCkNCg0KZmlsdF9pbGV1bV9hc3ZfdGFiIDwtIGRhdGFfZmlsdFtbMV1dOyBhbHBoYV9pbGV1bV9hc3ZfdGFiIDwtIGZpbHRfaWxldW1fYXN2X3RhYg0KZmlsdF9pbGV1bV90YXhhX3RhYiA8LSBkYXRhX2ZpbHRbWzJdXTsgYWxwaGFfaWxldW1fdGF4YV90YWIgPC0gZmlsdF9pbGV1bV90YXhhX3RhYg0KZmlsdF9pbGV1bV9tZXRhZGF0YSA8LSBkYXRhX2ZpbHRbWzNdXTsgYWxwaGFfaWxldW1fbWV0YWRhdGEgPC0gZmlsdF9pbGV1bV9tZXRhZGF0YQ0KDQpzZXFfc3RlcCA8LSBkaW0oZmlsdF9pbGV1bV9hc3ZfdGFiKVsxXQ0KYGBgDQoNCipOZWFyWmVyb1ZhcioNCg0KYGBge3J9DQpkYXRhX2ZpbHQgPC0gbmVhcnplcm92YXJfZmlsdGVyaW5nKGZpbHRfaWxldW1fYXN2X3RhYiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfaWxldW1fdGF4YV90YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfaWxldW1fbWV0YWRhdGEpDQoNCmZpbHRfaWxldW1fYXN2X3RhYiA8LSBkYXRhX2ZpbHRbWzFdXQ0KZmlsdF9pbGV1bV90YXhhX3RhYiA8LSBkYXRhX2ZpbHRbWzJdXQ0KbmVhcnplcm9fc3RlcCA8LSBkaW0oZmlsdF9pbGV1bV9hc3ZfdGFiKVsxXQ0KYGBgDQoNCg0KIyBEWVNCSU9TSVMgSU5ERVgNCg0KIyMgSWxldW0NCg0KKipBbGwgaW5kaWNlcyoqDQoNCmBgYHtyLGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KbWV0YWRhdGFfaWxldW1fbWVsdGVkIDwtIG1lbHQobWV0YWRhdGFfaWxldW0gJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KCJHcm91cCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkeXNfdW5maWx0ZXJlZF9hc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkeXNfdW5maWx0ZXJlZF9nZW51cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImR5c19maWx0ZXJlZF9hc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkeXNfZmlsdGVyZWRfZ2VudXMiKSkNCg0KcF9pbCA8LSBnZ3Bsb3QobWV0YWRhdGFfaWxldW1fbWVsdGVkKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKHg9R3JvdXAsIHk9dmFsdWUpLG91dGxpZXJzID0gRkFMU0UpICsgDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLGhlaWdodCA9IDAsYWVzKHg9R3JvdXAsIHk9dmFsdWUsIGNvbG9yPUdyb3VwKSwgDQogICAgICAgICAgICAgIHNpemU9MikgKw0KICBmYWNldF93cmFwKH52YXJpYWJsZSwgbmNvbCA9IDQsc2NhbGVzID0gImZyZWUiKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAwKSkgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiMzMDlmODciLCIjZjljNjc1IiwiI0YwODA4MCIsIiNBMDAwMDAiKSkgKyANCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjMzA5Zjg3IiwiI2Y5YzY3NSIsIiNGMDgwODAiLCIjQTAwMDAwIikpDQoNCnBfaWwgICAgDQpgYGANCg0KKipGSUxURVJFRCBHRU5VUyoqDQoNCmBgYHtyfQ0KZHlzX2xpbWl0IDwtIG1heChtZXRhZGF0YV9pbGV1bSRkeXNfZmlsdGVyZWRfZ2VudXMsbmEucm0gPSBUUlVFKSArIDAuNiptYXgobWV0YWRhdGFfaWxldW0kZHlzX2ZpbHRlcmVkX2dlbnVzLG5hLnJtID0gVFJVRSkNCmR5c19taW5fbGltaXQgPC0gIG1pbihtZXRhZGF0YV9pbGV1bSRkeXNfZmlsdGVyZWRfZ2VudXMpDQoNCnBfaWxfZHlzX2dlbnVzIDwtIGdncGxvdChtZXRhZGF0YV9pbGV1bSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShgTURJYD1keXNfZmlsdGVyZWRfZ2VudXMpKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKHg9R3JvdXAsIHk9YE1ESWApLA0KICAgICAgICAgICAgICAgb3V0bGllcnMgPSBGQUxTRSkgKyANCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjMsaGVpZ2h0ID0gMCwNCiAgICAgICAgICAgICAgYWVzKHg9R3JvdXAsIHk9YE1ESWAsIGNvbG9yPUdyb3VwLHNoYXBlPUNvdW50cnkpLA0KICAgICAgICAgICAgICBzaXplPTEuNSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAwKSkgICsgDQogIHlsaW0oZHlzX21pbl9saW1pdCxkeXNfbGltaXQpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjMzA5Zjg3IiwiI2Y5YzY3NSIsIiNGMDgwODAiLCIjQTAwMDAwIikpICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzMwOWY4NyIsIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LGZhY2UgPSAiYm9sZCIsdmp1c3QgPSAwLjUpKQ0KDQpwX2lsX2R5c19nZW51cw0KYGBgDQoNCiMjIENvbG9uDQoNCioqQWxsIGluZGljZXMqKg0KDQpgYGB7cixmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NH0NCmNvbG9uX21ldGFkYXRhX21lbHRlZCA8LSBtZWx0KG1ldGFkYXRhX2NvbG9uICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCgiR3JvdXAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZHlzX3VuZmlsdGVyZWRfYXN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZHlzX3VuZmlsdGVyZWRfZ2VudXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkeXNfZmlsdGVyZWRfYXN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZHlzX2ZpbHRlcmVkX2dlbnVzIikpDQpwX2NvbCA8LSBnZ3Bsb3QoY29sb25fbWV0YWRhdGFfbWVsdGVkKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKHg9R3JvdXAsIHk9dmFsdWUpLG91dGxpZXJzID0gRkFMU0UpICsgDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLGhlaWdodCA9IDAsDQogICAgICAgICAgICAgIGFlcyh4PUdyb3VwLCB5PXZhbHVlLCBjb2xvcj1Hcm91cCksDQogICAgICAgICAgICAgIHNpemU9MikgKw0KICBmYWNldF93cmFwKH52YXJpYWJsZSwgbmNvbCA9IDQsc2NhbGVzID0gImZyZWUiKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAwKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzMwOWY4NyIsIiNmOWM2NzUiLCIjRjA4MDgwIiwiI0EwMDAwMCIpKSArIA0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiMzMDlmODciLCIjZjljNjc1IiwiI0YwODA4MCIsIiNBMDAwMDAiKSkNCg0KcF9jb2wgIA0KYGBgDQoNCioqRklMVEVSRUQgR0VOVVMqKg0KDQpgYGB7cn0NCmR5c19saW1pdCA8LSBtYXgobWV0YWRhdGFfY29sb24kZHlzX2ZpbHRlcmVkX2dlbnVzLG5hLnJtID0gVFJVRSkgKyAwLjYqbWF4KG1ldGFkYXRhX2NvbG9uJGR5c19maWx0ZXJlZF9nZW51cyxuYS5ybSA9IFRSVUUpDQpkeXNfbWluX2xpbWl0IDwtICBtaW4obWV0YWRhdGFfY29sb24kZHlzX2ZpbHRlcmVkX2dlbnVzKQ0KDQpwX2NvbF9keXNfZ2VudXMgPC0gZ2dwbG90KG1ldGFkYXRhX2NvbG9uICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShgTURJYD1keXNfZmlsdGVyZWRfZ2VudXMpKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKHg9R3JvdXAsIHk9YE1ESWApLG91dGxpZXJzID0gRkFMU0UpICsgDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4zLGhlaWdodCA9IDAsDQogICAgICAgICAgICAgIGFlcyh4PUdyb3VwLCB5PWBNRElgLCBjb2xvcj1Hcm91cCxzaGFwZT1Db3VudHJ5KSwNCiAgICAgICAgICAgICAgc2l6ZT0xKSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJibGFjayIsIGZpbGwgPSBOQSwgc2l6ZSA9IDApKSsNCiAgeWxpbShkeXNfbWluX2xpbWl0LGR5c19saW1pdCkgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiMzMDlmODciLCIjZjljNjc1IiwiI0YwODA4MCIsIiNBMDAwMDAiKSkgKyANCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjMzA5Zjg3IiwiI2Y5YzY3NSIsIiNGMDgwODAiLCIjQTAwMDAwIikpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsZmFjZSA9ICJib2xkIix2anVzdCA9IDAuNSkpDQoNCnBfY29sX2R5c19nZW51cw0KYGBgDQoNCiMjIExpbmVhciBtb2RlbHMNCg0KKipJbGV1bSoqDQoNCmBgYHtyfQ0KcmVzdWx0c19tb2RlbCA8LSBwYWlyd2lzZS5sbSgNCiAgZm9ybXVsYSA9ICJkeXNfZmlsdGVyZWRfZ2VudXMgfiBHcm91cCIsDQogIGZhY3RvcnM9bWV0YWRhdGFfaWxldW0kR3JvdXAsDQogIGRhdGE9bWV0YWRhdGFfaWxldW0pDQoNCmtuaXRyOjprYWJsZShyZXN1bHRzX21vZGVsW1sxXV1bLGMoIkVzdGltYXRlIiwgInAuYWRqIiwic2lnIildLA0KICAgICAgICAgICAgIGRpZ2l0cz0zLGNhcHRpb249IlJlc3VsdHMgb2YgbGluZWFyIG1vZGVsaW5nIikNCmBgYA0KDQpgYGB7cn0NCnJlc3VsdHNfbW9kZWwgPC0gcGFpcndpc2UubG1lcigNCiAgZm9ybXVsYSA9ICJkeXNfZmlsdGVyZWRfZ2VudXMgfiBHcm91cCArICgxfFBhdGllbnRJRCkiLA0KICBmYWN0b3JzPW1ldGFkYXRhX2NvbG9uJEdyb3VwLA0KICBkYXRhPW1ldGFkYXRhX2NvbG9uKQ0KDQprbml0cjo6a2FibGUocmVzdWx0c19tb2RlbFtbMV1dWyxjKCJFc3RpbWF0ZSIsICJwLmFkaiIsInNpZyIpXSwNCiAgICAgICAgICAgICBkaWdpdHM9MyxjYXB0aW9uPSJSZXN1bHRzIG9mIGxpbmVhciBtaXhlZC1lZmZlY3QgbW9kZWxpbmciKQ0KDQpgYGANCg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmR5cyA8LSBnZ2FycmFuZ2UocF9pbCArIGdndGl0bGUoIklsZXVtIiksDQogICAgICAgICAgICAgICAgIHBfY29sICsgZ2d0aXRsZSgiQ29sb24iKSxucm93ID0gMikNCg0KcGRmKCJyZXN1bHRzL2NsaW5pY2FsL2R5c2Jpb3Npc19pbmRleF9ib3hwbG90cy5wZGYiLA0KICAgIGhlaWdodCA9IDgsd2lkdGggPTE1KQ0KDQpkeXMNCmRldi5vZmYoKQ0KYGBgDQoNCiMgQUxQSEEgRElWRVJTSVRZDQoNCmBgYHtyfQ0KYWxwaGFfZGl2X3Bsb3RzIDwtIGxpc3QoKQ0KYWxwaGFfYm94cGxvdHMgPC0gbGlzdCgpDQpjb3JycyA8LSBsaXN0KCkNCmBgYA0KDQojIyBJbGV1bQ0KDQpgYGB7cn0NCmlsZXVtX21ldGFkYXRhX2FscGhhX2ZpbmFsIDwtIG1ldGFkYXRhX2lsZXVtICU+JSANCiAgZHBseXI6OnNlbGVjdCgiU2FtcGxlSUQiLCAiUGF0aWVudElEIiwiR3JvdXAiLA0KICAgICAgICAgICAgICAgICJPYnNlcnZlIiwiU2hhbm5vbiIsIlNpbXBzb24iLCJQaWVsb3UiLA0KICAgICAgICAgICAgICAgICJkeXNfZmlsdGVyZWRfYXN2IiwgImR5c19maWx0ZXJlZF9nZW51cyIpDQpgYGANCg0KYGBge3J9DQp2YXJpYWJsZXMgPC0gYygiT2JzZXJ2ZSIsIlNoYW5ub24iLCJTaW1wc29uIiwiUGllbG91IikNCg0KZm9yIChjbGluaWNhbF92YXJpYWJsZSBpbiB2YXJpYWJsZXMpew0KICANCiAgIyBJTEVVTQ0KICAjIGJveHBsb3QNCiAgcCA8LSBjbGluaWNhbF9ib3hwbG90KGlsZXVtX21ldGFkYXRhX2FscGhhX2ZpbmFsLA0KICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlPWNsaW5pY2FsX3ZhcmlhYmxlKQ0KDQogIGFscGhhX2JveHBsb3RzW1twYXN0ZTAoImlsZXVtXyIsY2xpbmljYWxfdmFyaWFibGUpXV0gPC0gcA0KICANCiAgIyBjb3JyZWxhdGlvbg0KICAjIyBBU1YNCiAgbGV2ZWw9IkFTViINCiAgY29yciA8LSBjbGluaWNhbF9jb3JyZWxhdGlvbigNCiAgICBpbGV1bV9tZXRhZGF0YV9hbHBoYV9maW5hbCwNCiAgICBjbGluaWNhbF92YXJpYWJsZSwNCiAgICBsZXZlbCkNCiAgDQogIGNvcnJzW1twYXN0ZTAoImlsZXVtX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIGNvcnINCiAgDQogIHAgPC0gY2xpbmljYWxfc2NhdHRlcihjb3JyLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWxldW1fbWV0YWRhdGFfYWxwaGFfZmluYWwsDQogICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF92YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsKQ0KICANCiAgYWxwaGFfZGl2X3Bsb3RzW1twYXN0ZTAoImlsZXVtX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCg0KICAjIyBHZW51cw0KICBsZXZlbD0iZ2VudXMiDQogIGNvcnIgPC0gY2xpbmljYWxfY29ycmVsYXRpb24oaWxldW1fbWV0YWRhdGFfYWxwaGFfZmluYWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwpDQogIGNvcnJzW1twYXN0ZTAoImlsZXVtX2dlbnVzXyIsY2xpbmljYWxfdmFyaWFibGUpXV0gPC0gY29ycg0KICANCiAgcCA8LSBjbGluaWNhbF9zY2F0dGVyKGNvcnIsDQogICAgICAgICAgICAgICAgICAgICAgICBpbGV1bV9tZXRhZGF0YV9hbHBoYV9maW5hbCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3ZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwpDQogIA0KICBhbHBoYV9kaXZfcGxvdHNbW3Bhc3RlMCgiaWxldW1fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBwDQp9DQpgYGANCg0KIyMjIEJveHBsb3QNCg0KYGBge3IsZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9DQphbHBoYV9ib3hwbG90c19pbGV1bSA8LSBnZ2FycmFuZ2UoDQogIHBsb3RsaXN0ID0gYWxwaGFfYm94cGxvdHNbZ3JlcGwoImlsZXVtIixuYW1lcyhhbHBoYV9ib3hwbG90cykpXSwNCiAgbmNvbCA9IDQsDQogIGNvbW1vbi5sZWdlbmQgPSBUUlVFLCANCiAgbGVnZW5kID0gInJpZ2h0IikNCg0KYWxwaGFfYm94cGxvdHNfaWxldW0NCmBgYA0KDQoNCiMjIENvbG9uDQoNCmBgYHtyfQ0KY29sb25fbWV0YWRhdGFfYWxwaGFfZmluYWwgPC0gbWV0YWRhdGFfY29sb24gJT4lIA0KICBkcGx5cjo6c2VsZWN0KCJTYW1wbGVJRCIsICJQYXRpZW50SUQiLCAiR3JvdXAiLA0KICAgICAgICAgICAgICAgICJPYnNlcnZlIiwiU2hhbm5vbiIsIlNpbXBzb24iLCJQaWVsb3UiLA0KICAgICAgICAgICAgICAgICJkeXNfZmlsdGVyZWRfYXN2IiwiZHlzX2ZpbHRlcmVkX2dlbnVzIikNCmBgYA0KDQpgYGB7cn0NCnZhcmlhYmxlcyA8LSBjKCJPYnNlcnZlIiwiU2hhbm5vbiIsIlNpbXBzb24iLCJQaWVsb3UiKQ0KDQpmb3IgKGNsaW5pY2FsX3ZhcmlhYmxlIGluIHZhcmlhYmxlcyl7DQoNCiAgIyBDT0xVTU4NCiAgcCA8LSBjbGluaWNhbF9ib3hwbG90KGNvbG9uX21ldGFkYXRhX2FscGhhX2ZpbmFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGU9Y2xpbmljYWxfdmFyaWFibGUpDQoNCiAgYWxwaGFfYm94cGxvdHNbW3Bhc3RlMCgiY29sb25fIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBwDQogIA0KICAjIGNvcnJlbGF0aW9uDQogICMjIEFTVg0KICBsZXZlbD0iQVNWIg0KICBjb3JyIDwtIGNsaW5pY2FsX2NvcnJlbGF0aW9uKGNvbG9uX21ldGFkYXRhX2FscGhhX2ZpbmFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3ZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQ9ImNvbG9uIikNCiAgDQogIGNvcnJzW1twYXN0ZTAoImNvbG9uX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIGNvcnINCiAgDQogIHAgPC0gY2xpbmljYWxfc2NhdHRlcihjb3JyLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb25fbWV0YWRhdGFfYWxwaGFfZmluYWwsDQogICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF92YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsKQ0KICANCiAgYWxwaGFfZGl2X3Bsb3RzW1twYXN0ZTAoImNvbG9uX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCg0KICAjIyBHZW51cw0KICBsZXZlbD0iZ2VudXMiDQogIGNvcnIgPC0gY2xpbmljYWxfY29ycmVsYXRpb24oY29sb25fbWV0YWRhdGFfYWxwaGFfZmluYWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudD0iY29sb24iKQ0KICANCiAgY29ycnNbW3Bhc3RlMCgiY29sb25fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBjb3JyDQogIA0KICBwIDwtIGNsaW5pY2FsX3NjYXR0ZXIoY29yciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9uX21ldGFkYXRhX2FscGhhX2ZpbmFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCkNCiAgDQogIGFscGhhX2Rpdl9wbG90c1tbcGFzdGUwKCJjb2xvbl9nZW51c18iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCn0NCg0KYGBgDQoNCiMjIyBCb3hwbG90DQoNCmBgYHtyLGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD00fQ0KYWxwaGFfYm94cGxvdHNfY29sb24gPC0gZ2dhcnJhbmdlKA0KICBwbG90bGlzdCA9IGFscGhhX2JveHBsb3RzW2dyZXBsKCJjb2xvbiIsbmFtZXMoYWxwaGFfYm94cGxvdHMpKV0sDQogIG5jb2wgPSA0LA0KICBjb21tb24ubGVnZW5kID0gVFJVRSwgDQogIGxlZ2VuZCA9ICJyaWdodCIpDQoNCmFscGhhX2JveHBsb3RzX2NvbG9uDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQphbHBoYV9ib3hwbG90IDwtIGdnYXJyYW5nZShhbHBoYV9ib3hwbG90c19pbGV1bSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhX2JveHBsb3RzX2NvbG9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDIpDQpwZGYoInJlc3VsdHMvY2xpbmljYWwvYWxwaGFfZGl2ZXJzaXR5X2JveHBsb3RzLnBkZiIsaGVpZ2h0ID0gNyx3aWR0aCA9MTcpDQphbHBoYV9ib3hwbG90DQpkZXYub2ZmKCkNCmBgYA0KDQojIyBTY2F0dGVycGxvdHMNCg0KKipBU1YqKg0KDQpgYGB7cixmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9OH0NCmFscGhhX2R5c19hc3YgPC0gZ2dhcnJhbmdlKA0KICBwbG90bGlzdCA9IGFscGhhX2Rpdl9wbG90c1tncmVwbCgiYXN2IixuYW1lcyhhbHBoYV9kaXZfcGxvdHMpKV0sDQogIG5jb2w9NCwNCiAgbnJvdz0yLA0KICBjb21tb24ubGVnZW5kID0gVFJVRSwNCiAgbGVnZW5kPSJyaWdodCIpDQoNCmFscGhhX2R5c19hc3YNCmBgYA0KDQoqKkdlbnVzKioNCg0KYGBge3IsZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTh9DQphbHBoYV9keXNfZ2VudXMgPC0gZ2dhcnJhbmdlKA0KICBwbG90bGlzdCA9IGFscGhhX2Rpdl9wbG90c1tncmVwbCgiZ2VudXMiLG5hbWVzKGFscGhhX2Rpdl9wbG90cykpXSwNCiAgbmNvbD00LA0KICBucm93PTIsDQogIGNvbW1vbi5sZWdlbmQgPSBUUlVFLA0KICBsZWdlbmQ9InJpZ2h0IikNCg0KYWxwaGFfZHlzX2dlbnVzDQpgYGANCg0KKipTYXZpbmcgcGxvdHMqKg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnBkZigicmVzdWx0cy9jbGluaWNhbC9hbHBoYV9kaXZlcnNpdHkucGRmIixoZWlnaHQgPSA4LHdpZHRoID0xOCkNCmFscGhhX2R5c19hc3YNCmRldi5vZmYoKQ0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KcGRmKCJyZXN1bHRzL2NsaW5pY2FsL2FscGhhX2RpdmVyc2l0eV9nZW51cy5wZGYiLGhlaWdodCA9IDgsd2lkdGggPTE4KQ0KYWxwaGFfZHlzX2dlbnVzDQpkZXYub2ZmKCkNCmBgYA0KDQojIyBMaW5lYXIgbW9kZWxzDQoNCiMjIyBSaWNobmVzcw0KDQoqKklsZXVtKioNCg0KYGBge3J9DQpyZXN1bHRzX21vZGVsIDwtIHBhaXJ3aXNlLmxtKGZvcm11bGEgPSAiT2JzZXJ2ZSB+IEdyb3VwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0b3JzPW1ldGFkYXRhX2lsZXVtJEdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9bWV0YWRhdGFfaWxldW0pDQoNCmtuaXRyOjprYWJsZShyZXN1bHRzX21vZGVsW1sxXV1bLGMoIkVzdGltYXRlIiwgInAuYWRqIiwic2lnIildLA0KICAgICAgICAgICAgIGRpZ2l0cz0zLGNhcHRpb249IlJlc3VsdHMgb2YgbGluZWFyIG1vZGVsaW5nIikNCmBgYA0KDQoqKkNvbG9uKioNCg0KYGBge3J9DQpyZXN1bHRzX21vZGVsIDwtIHBhaXJ3aXNlLmxtZXIoDQogIGZvcm11bGEgPSAiT2JzZXJ2ZSB+IEdyb3VwICsgKDF8UGF0aWVudCkiLA0KICBmYWN0b3JzPW1ldGFkYXRhX2NvbG9uJEdyb3VwLA0KICBkYXRhPW1ldGFkYXRhX2NvbG9uICU+JSBtdXRhdGUoUGF0aWVudD1QYXRpZW50SUQpKQ0KDQprbml0cjo6a2FibGUocmVzdWx0c19tb2RlbFtbMV1dWyxjKCJFc3RpbWF0ZSIsICJwLmFkaiIsInNpZyIpXSwNCiAgICAgICAgICAgICBkaWdpdHM9MyxjYXB0aW9uPSJSZXN1bHRzIG9mIGxpbmVhciBtb2RlbGluZyIpDQpgYGANCg0KIyMjIFNoYW5ub24NCg0KKipJbGV1bSoqDQoNCmBgYHtyfQ0KcmVzdWx0c19tb2RlbCA8LSBwYWlyd2lzZS5sbShmb3JtdWxhID0gIlNoYW5ub24gfiBHcm91cCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjdG9ycz1tZXRhZGF0YV9pbGV1bSRHcm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPW1ldGFkYXRhX2lsZXVtKQ0KDQprbml0cjo6a2FibGUocmVzdWx0c19tb2RlbFtbMV1dWyxjKCJFc3RpbWF0ZSIsICJwLmFkaiIsInNpZyIpXSwNCiAgICAgICAgICAgICBkaWdpdHM9MyxjYXB0aW9uPSJSZXN1bHRzIG9mIGxpbmVhciBtb2RlbGluZyIpDQpgYGANCg0KKipDb2xvbioqDQoNCmBgYHtyfQ0KcmVzdWx0c19tb2RlbCA8LSBwYWlyd2lzZS5sbWVyKA0KICBmb3JtdWxhID0gIlNoYW5ub24gfiBHcm91cCArICgxfFBhdGllbnQpIiwNCiAgZmFjdG9ycz1tZXRhZGF0YV9jb2xvbiRHcm91cCwNCiAgZGF0YT1tZXRhZGF0YV9jb2xvbiAlPiUgbXV0YXRlKFBhdGllbnQ9UGF0aWVudElEKSkNCg0Ka25pdHI6OmthYmxlKHJlc3VsdHNfbW9kZWxbWzFdXVssYygiRXN0aW1hdGUiLCAicC5hZGoiLCJzaWciKV0sDQogICAgICAgICAgICAgZGlnaXRzPTMsY2FwdGlvbj0iUmVzdWx0cyBvZiBsaW5lYXIgbW9kZWxpbmciKQ0KYGBgDQoNCg0KIyBNREkgYW5kIGNsaW5pY2FsIHZhcmlhYmxlcw0KDQpgYGB7cn0NCg0KdmFyaWFibGVzIDwtIGMoIkFMUCIsImxvZ19BTFAiLA0KICAgICAgICAgICAgICAgIkdHVCIsImxvZ19HR1QiLA0KICAgICAgICAgICAgICAgIkFMVCIsImxvZ19BTFQiLA0KICAgICAgICAgICAgICAgIkFTVCIsImxvZ19BU1QiLA0KICAgICAgICAgICAgICAgIkJpbGlydWJpbiIsImxvZ19CaWxpcnViaW4iLA0KICAgICAgICAgICAgICAgIklOUiIsDQogICAgICAgICAgICAgICAiQWxidW1pbiIsDQogICAgICAgICAgICAgICAiUGxhdGVsZXRzIiwNCiAgICAgICAgICAgICAgICJDcmVhdGluaW5lIiwibG9nX0NyZWF0aW5pbmUiLA0KICAgICAgICAgICAgICAgIkZJQjRfc2NvcmUiLA0KICAgICAgICAgICAgICAgIkFQUklfc2NvcmUiLA0KICAgICAgICAgICAgICAgIk1BWU9fUFNDIiwNCiAgICAgICAgICAgICAgICJBT01fc2NvcmUiLA0KICAgICAgICAgICAgICAgIkZlY2FsLmNhbHByb3RlY3RpbiIsDQogICAgICAgICAgICAgICAiTmFuY3lfbWF4IiwNCiAgICAgICAgICAgICAgICJlTUFZTyIsDQogICAgICAgICAgICAgICAiTUFZT19kYWkiDQogICAgICAgICAgICAgICApDQoNCg0KYGBgDQoNCg0KYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTN9DQpib3hwbG90c19wbG90cyA8LSBsaXN0KCkNCmNvcnJzIDwtIGxpc3QoKQ0KY2xpbmljYWxfcGxvdHMgPC0gbGlzdCgpDQoNCmZvciAoY2xpbmljYWxfdmFyaWFibGUgaW4gdmFyaWFibGVzKXsNCiAgDQogIGlmIChncmVwbCgibG9nXyIsY2xpbmljYWxfdmFyaWFibGUpKSB7DQogICBtZXRhZGF0YV9mb3JfYm94cGxvdHNbLGNsaW5pY2FsX3ZhcmlhYmxlXSA8LSBsb2coYXMubnVtZXJpYyhtZXRhZGF0YV9mb3JfYm94cGxvdHNbLGdzdWIoImxvZ18iLCIiLGNsaW5pY2FsX3ZhcmlhYmxlKV0pKQ0KICAgDQogICBtZXRhZGF0YV9pbGV1bVssY2xpbmljYWxfdmFyaWFibGVdIDwtIGxvZyhhcy5udW1lcmljKG1ldGFkYXRhX2lsZXVtWyxnc3ViKCJsb2dfIiwiIixjbGluaWNhbF92YXJpYWJsZSldKSkNCiAgIG1ldGFkYXRhX2NvbG9uWyxjbGluaWNhbF92YXJpYWJsZV0gPC0gbG9nKGFzLm51bWVyaWMobWV0YWRhdGFfY29sb25bLGdzdWIoImxvZ18iLCIiLGNsaW5pY2FsX3ZhcmlhYmxlKV0pKQ0KICB9DQogIA0KICAjIGJveHBsb3QNCiAgcCA8LSBjbGluaWNhbF9ib3hwbG90KG1ldGFkYXRhX2Zvcl9ib3hwbG90cywNCiAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZT1jbGluaWNhbF92YXJpYWJsZSkNCg0KICBib3hwbG90c19wbG90c1tbY2xpbmljYWxfdmFyaWFibGVdXSA8LSBwDQogIA0KICAjIElMRVVNDQogICMgY29ycmVsYXRpb24NCiAgDQogICMjIEFTVg0KICBsZXZlbD0iQVNWIg0KICBjb3JyIDwtIGNsaW5pY2FsX2NvcnJlbGF0aW9uKG1ldGFkYXRhX2lsZXVtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3ZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsKQ0KICANCiAgY29ycnNbW3Bhc3RlMCgiaWxldW1fYXN2XyIsY2xpbmljYWxfdmFyaWFibGUpXV0gPC0gY29ycg0KICANCiAgcCA8LSBjbGluaWNhbF9zY2F0dGVyKGNvcnIsDQogICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YV9pbGV1bSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3ZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwpDQogIGNsaW5pY2FsX3Bsb3RzW1twYXN0ZTAoImlsZXVtX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCg0KICAjIyBHZW51cw0KICBsZXZlbD0iZ2VudXMiDQogIGNvcnIgPC0gY2xpbmljYWxfY29ycmVsYXRpb24obWV0YWRhdGFfaWxldW0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwpDQogIGNvcnJzW1twYXN0ZTAoImlsZXVtX2dlbnVzXyIsY2xpbmljYWxfdmFyaWFibGUpXV0gPC0gY29ycg0KICANCiAgcCA8LSBjbGluaWNhbF9zY2F0dGVyKGNvcnIsDQogICAgICAgICAgICAgICAgICAgICAgICBtZXRhZGF0YV9pbGV1bSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3ZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwpDQogIGNsaW5pY2FsX3Bsb3RzW1twYXN0ZTAoImlsZXVtX2dlbnVzXyIsY2xpbmljYWxfdmFyaWFibGUpXV0gPC0gcA0KICANCiAgIyBjb3JyZWxhdGlvbg0KICAjIyBBU1YNCiAgbGV2ZWw9IkFTViINCiAgY29yciA8LSBjbGluaWNhbF9jb3JyZWxhdGlvbihtZXRhZGF0YV9jb2xvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF92YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50PSJjb2xvbiIpDQogIA0KICBjb3Jyc1tbcGFzdGUwKCJjb2xvbl9hc3ZfIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBjb3JyDQogIA0KICBwIDwtIGNsaW5pY2FsX3NjYXR0ZXIoY29yciwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhX2NvbG9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCkNCiAgDQogIGNsaW5pY2FsX3Bsb3RzW1twYXN0ZTAoImNvbG9uX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCg0KICAjIyBHZW51cw0KICBsZXZlbD0iZ2VudXMiDQogIGNvcnIgPC0gY2xpbmljYWxfY29ycmVsYXRpb24obWV0YWRhdGFfY29sb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudD0iY29sb24iKQ0KICANCiAgY29ycnNbW3Bhc3RlMCgiY29sb25fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBjb3JyDQogIA0KICBwIDwtIGNsaW5pY2FsX3NjYXR0ZXIoY29yciwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhX2NvbG9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCkNCiAgDQogIGNsaW5pY2FsX3Bsb3RzW1twYXN0ZTAoImNvbG9uX2dlbnVzXyIsY2xpbmljYWxfdmFyaWFibGUpXV0gPC0gcA0KfQ0KDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQp2YXJpYWJsZXMgPC0gYygiTmFuY3lfbWF4IiwiZU1BWU8iLCJNQVlPX2RhaSIpDQpgYGANCg0KYGBge3IsZXZhbD1GQUxTRX0NCmZvciAoY2xpbmljYWxfdmFyaWFibGUgaW4gdmFyaWFibGVzKXsNCiAgIyBDT05USU5HRU5DWSBUQUJMRQ0KICBwIDwtIGNsaW5pY2FsX2NvbnRpbmdlbmN5KG1ldGFkYXRhX2Zvcl9ib3hwbG90cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF92YXJpYWJsZSkNCiAgYm94cGxvdHNfcGxvdHNbW2NsaW5pY2FsX3ZhcmlhYmxlXV0gPC0gcA0KICANCiAgIyBJTEVVTQ0KICAjIyBBU1YNCiAgcCA8LSBjbGluaWNhbF9ib3hwbG90X2NhdGVnb3JpY2FsKG1ldGFkYXRhX2lsZXVtLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbCA9ICJBU1YiKQ0KICBjbGluaWNhbF9wbG90c1tbcGFzdGUwKCJpbGV1bV9hc3ZfIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBwDQogIA0KICAjIyBHRU5VUw0KICBwIDwtIGNsaW5pY2FsX2JveHBsb3RfY2F0ZWdvcmljYWwobWV0YWRhdGFfaWxldW0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF92YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsID0gIkdlbnVzIikNCiAgY2xpbmljYWxfcGxvdHNbW3Bhc3RlMCgiaWxldW1fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSldXSA8LSBwDQogIA0KICAjIENPTE9ODQogICMjIEFTVg0KICBwIDwtIGNsaW5pY2FsX2JveHBsb3RfY2F0ZWdvcmljYWwobWV0YWRhdGFfaWxldW0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF92YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsID0gIkFTViIpDQogIGNsaW5pY2FsX3Bsb3RzW1twYXN0ZTAoImNvbG9uX2Fzdl8iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCiAgDQogICMjIEdFTlVTDQogIHAgPC0gY2xpbmljYWxfYm94cGxvdF9jYXRlZ29yaWNhbChtZXRhZGF0YV9pbGV1bSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3ZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSAiR2VudXMiKQ0KICBjbGluaWNhbF9wbG90c1tbcGFzdGUwKCJjb2xvbl9nZW51c18iLGNsaW5pY2FsX3ZhcmlhYmxlKV1dIDwtIHANCn0NCg0KYGBgDQoNCiMjIEJveHBsb3RzDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bGVuZ3RoKGJveHBsb3RzX3Bsb3RzKSl7DQogIG5hbWUgPC0gbmFtZXMoYm94cGxvdHNfcGxvdHMpW2ldDQogIHBsIDwtIGJveHBsb3RzX3Bsb3RzW1tpXV0NCiAgcGwgPC0gcGwgKyBnZ3RpdGxlKG5hbWUpDQogIGJveHBsb3RzX3Bsb3RzW1tpXV0gPC0gcGwNCn0NCmJ4IDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IGJveHBsb3RzX3Bsb3RzLCBuY29sID0gNCxucm93PTQpDQpgYGANCg0KYGBge3IsZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEyfQ0KYngNCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnBkZigicmVzdWx0cy9jbGluaWNhbC9jbGluaWNhbF9kYXRhX2JveHBsb3RzLnBkZiIsaGVpZ2h0ID0gMTUsd2lkdGggPTEzKQ0KYngNCmRldi5vZmYoKQ0KYGBgDQoNCiMjIENsaW5pY2FsIHBsb3RzIC0gTURJIH4gY2xpbmljYWwgdmFyaWFibGUNCg0KYGBge3J9DQpmb3IgKGkgaW4gMTpsZW5ndGgoY2xpbmljYWxfcGxvdHMpKXsNCiAgbmFtZSA8LSBuYW1lcyhjbGluaWNhbF9wbG90cylbaV0NCiAgcGwgPC0gY2xpbmljYWxfcGxvdHNbW2ldXQ0KICBwbCA8LSBwbCArIGdndGl0bGUobmFtZSkNCiAgY2xpbmljYWxfcGxvdHNbW2ldXSA8LSBwbA0KfQ0KDQpjbF9hc3YgPC0gZ2dhcnJhbmdlKA0KICBwbG90bGlzdCA9IGNsaW5pY2FsX3Bsb3RzW2dyZXBsKCJhc3YiLG5hbWVzKGNsaW5pY2FsX3Bsb3RzKSldLA0KICBuY29sPTQsbnJvdz04KQ0KDQpjbF9nZW51cyA8LSBnZ2FycmFuZ2UocGxvdGxpc3QgPSBjbGluaWNhbF9wbG90c1tncmVwbCgiZ2VudXMiLG5hbWVzKGNsaW5pY2FsX3Bsb3RzKSldLA0KICAgICAgICAgIG5jb2w9NCxucm93PTgpDQoNCmBgYA0KDQpgYGB7cixmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjJ9DQpjbF9hc3YNCmBgYA0KDQpgYGB7cixmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MjJ9DQpjbF9nZW51cw0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KcGRmKCJyZXN1bHRzL2NsaW5pY2FsL2NvcnJlbGF0aW9uLnBkZiIsaGVpZ2h0ID0gMjIsd2lkdGggPTE4KQ0KY2xfYXN2DQpkZXYub2ZmKCkNCmBgYA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KcGRmKCJyZXN1bHRzL2NsaW5pY2FsL2NvcnJlbGF0aW9uX2dlbnVzLnBkZiIsaGVpZ2h0ID0gMjIsd2lkdGggPTE4KQ0KY2xfZ2VudXMNCmRldi5vZmYoKQ0KDQpgYGANCg0KIyMgSEVBVE1BUA0KDQojIyMgQVNWIGxldmVsDQoNCmBgYHtyfQ0KY29ycnNfaWxldW0gPC0gY29ycnNbZ3JlcGwoImlsZXVtX2FzdiIsbmFtZXMoY29ycnMpKV0NCmNvcnJzX2NvbG9uIDwtIGNvcnJzW2dyZXBsKCJjb2xvbl9hc3YiLG5hbWVzKGNvcnJzKSldDQpgYGANCg0KYGBge3IsZmlnLndpZHRoPTMsZmlnLmhlaWdodD03fQ0KcHJlcGFyZWRfbGlzdCA8LSBwcmVwYXJlX2Zvcl9oZWF0bWFwKGNvcnJzX2lsZXVtLGNvcnJzX2NvbG9uKQ0KcF9kZl9zaWdfbWRpIDwtIHByZXBhcmVkX2xpc3RbWzFdXQ0Kcl9kZl9tZGkgPC0gcHJlcGFyZWRfbGlzdFtbMl1dDQpgYGANCg0KYGBge3IsZmlnLndpZHRoPTQsZmlnLmhlaWdodD03fQ0Kcl9kZl9tZWx0X21kaSA8LSBtZWx0KHJfZGZfbWRpICU+JSByb3duYW1lc190b19jb2x1bW4oIlNlcUlEIikpDQpwX2RmX21lbHRfbWRpIDwtIG1lbHQocF9kZl9zaWdfbWRpICU+JSByb3duYW1lc190b19jb2x1bW4oIlNlcUlEIiksDQogICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9IGMoIlNlcUlEIikpDQoNCnJfZGZfbWVsdF9tZGkkU2VxSUQgPC0gZmFjdG9yKHJfZGZfbWVsdF9tZGkkU2VxSUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9dW5pcXVlKHJfZGZfbWVsdF9tZGkkU2VxSUQpKQ0KDQpwX2RmX21lbHRfbWRpJFNlcUlEIDwtIGZhY3RvcihwX2RmX21lbHRfbWRpJFNlcUlELA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPXVuaXF1ZShwX2RmX21lbHRfbWRpJFNlcUlEKSkNCg0Kcl9kZl9tZWx0X21kaSR2YXJpYWJsZSA8LSBmYWN0b3Iocl9kZl9tZWx0X21kaSR2YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1yZXYodW5pcXVlKHJfZGZfbWVsdF9tZGkkdmFyaWFibGUpKSkNCg0KcF9kZl9tZWx0X21kaSR2YXJpYWJsZSA8LSBmYWN0b3IocF9kZl9tZWx0X21kaSR2YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1yZXYodW5pcXVlKHBfZGZfbWVsdF9tZGkkdmFyaWFibGUpKSkNCg0KcCA8LSBnZ3Bsb3QoKSArIA0KICBnZW9tX3RpbGUoZGF0YT1yX2RmX21lbHRfbWRpLCBhZXMoU2VxSUQsIHZhcmlhYmxlLCBmaWxsPSB2YWx1ZSkpICArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobmFtZSA9ICJSaG8iLCBsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIiwgbWlkcG9pbnQgPSAwLGxpbWl0cyA9IGMoLTAuNywgMC43KSkgKyANCiAgZ2VvbV90ZXh0KGRhdGE9cF9kZl9tZWx0X21kaSxhZXMoeD1TZXFJRCx5PXZhcmlhYmxlLGxhYmVsPXZhbHVlLHZqdXN0PTAuNikpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyANCiAgeGxhYigiIikgKyB5bGFiKCIiKSAgKyANCiAgZ2d0aXRsZSgiTURJIikrIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSxmYWNlID0gImJvbGQiLHNpemUgPSAxNSkpDQoNCnANCmBgYA0KDQojIyMgR2VudXMgbGV2ZWwNCg0KYGBge3J9DQpjb3Jyc19pbGV1bSA8LSBjb3Jyc1tncmVwbCgiaWxldW1fZ2VudXMiLG5hbWVzKGNvcnJzKSldDQpjb3Jyc19jb2xvbiA8LSBjb3Jyc1tncmVwbCgiY29sb25fZ2VudXMiLG5hbWVzKGNvcnJzKSldDQpgYGANCg0KYGBge3J9DQpwcmVwYXJlZF9saXN0IDwtIHByZXBhcmVfZm9yX2hlYXRtYXAoY29ycnNfaWxldW0sY29ycnNfY29sb24pDQpwX2RmX3NpZ19tZGkgPC0gcHJlcGFyZWRfbGlzdFtbMV1dDQpyX2RmX21kaSA8LSBwcmVwYXJlZF9saXN0W1syXV0NCmBgYA0KDQpgYGB7cixmaWcud2lkdGg9NCxmaWcuaGVpZ2h0PTd9DQpyX2RmX21lbHRfbWRpIDwtIG1lbHQocl9kZl9tZGkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiU2VxSUQiKSkNCnBfZGZfbWVsdF9tZGkgPC0gbWVsdChwX2RmX3NpZ19tZGkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiU2VxSUQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gYygiU2VxSUQiKSkNCg0Kcl9kZl9tZWx0X21kaSRTZXFJRCA8LSBmYWN0b3Iocl9kZl9tZWx0X21kaSRTZXFJRCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz11bmlxdWUocl9kZl9tZWx0X21kaSRTZXFJRCkpDQoNCnBfZGZfbWVsdF9tZGkkU2VxSUQgPC0gZmFjdG9yKHBfZGZfbWVsdF9tZGkkU2VxSUQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9dW5pcXVlKHBfZGZfbWVsdF9tZGkkU2VxSUQpKQ0KDQpyX2RmX21lbHRfbWRpJHZhcmlhYmxlIDwtIGZhY3RvcihyX2RmX21lbHRfbWRpJHZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPXJldih1bmlxdWUocl9kZl9tZWx0X21kaSR2YXJpYWJsZSkpKQ0KDQpwX2RmX21lbHRfbWRpJHZhcmlhYmxlIDwtIGZhY3RvcihwX2RmX21lbHRfbWRpJHZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPXJldih1bmlxdWUocF9kZl9tZWx0X21kaSR2YXJpYWJsZSkpKQ0KDQpwX21kaSA8LSBnZ3Bsb3QoKSArIA0KICBnZW9tX3RpbGUoZGF0YT1yX2RmX21lbHRfbWRpLCBhZXMoU2VxSUQsIHZhcmlhYmxlLCBmaWxsPSB2YWx1ZSkpICArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobmFtZSA9ICJSaG8iLCBsb3cgPSAiYmx1ZSIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkIiwgbWlkcG9pbnQgPSAwLGxpbWl0cyA9IGMoLTAuNywgMC43KSkgKyANCiAgZ2VvbV90ZXh0KGRhdGE9cF9kZl9tZWx0X21kaSxhZXMoeD1TZXFJRCx5PXZhcmlhYmxlLGxhYmVsPXZhbHVlLHZqdXN0PTAuNikpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyANCiAgeGxhYigiIikgKyB5bGFiKCIiKSAgKyANCiAgZ2d0aXRsZSgiTURJIikrIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSxmYWNlID0gImJvbGQiLHNpemUgPSAxNSkpDQoNCnBfbWRpDQpgYGANCg0KIyBCYWN0ZXJpYSBhbmQgc2lnbmlmaWNhbnQgY2xpbmljYWwgdmFyaWFibGVzDQoNCiMjIFRlcm1pbmFsIGlsZXVtDQoNCiMjIyBHZW51cyBsZXZlbA0KDQpgYGB7cn0NCmxldmVsPSJnZW51cyINCmBgYA0KDQpBZ2dyZWdhdGlvbiwgZmlsdGVyaW5nDQoNCmBgYHtyfQ0KIyBBZ2dyZWdhdGlvbg0KZ2VudXNfZGF0YSA8LSBhZ2dyZWdhdGVfdGF4YShpbGV1bV9hc3ZfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbGV1bV90YXhhX3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4b25vbWljX2xldmVsPWxldmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcz1UUlVFKQ0KIyBGaWx0cmF0aW9uDQpmaWx0X2RhdGEgPC0gZmlsdGVyaW5nX3N0ZXBzKGdlbnVzX2RhdGFbWzFdXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VudXNfZGF0YVtbMl1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbGV1bV9tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxX2RlcHRoX3RocmVzaG9sZD0xMDAwMCkNCg0KZmlsdF9pbGV1bV9nZW51c190YWIgPC0gZmlsdF9kYXRhW1sxXV0NCmZpbHRfaWxldW1fZ2VudXNfdGF4YSA8LSBmaWx0X2RhdGFbWzJdXQ0KZmlsdF9pbGV1bV9tZXRhZGF0YV9nZW51cyA8LSBmaWx0X2RhdGFbWzNdXQ0KYGBgDQoNCmBgYHtyfQ0KcHNjX2VmZmVjdF9pbGV1bV9nZW51cyA8LSByZWFkLnhsc3goDQogICIuLi9yZXN1bHRzL1ExX2N6ZWNoL3VuaXZhcmlhdGVfYW5hbHlzaXMvc3VwcGxlbWVudHNfcHNjX2VmZmVjdF90ZXJtaW5hbF9pbGV1bS54bHN4IiwNCiAgc2hlZXQgPSAidGVybWluYWxfaWxldW0gZ2VudXMiKQ0KcHNjX3RheGEgPC0gcHNjX2VmZmVjdF9pbGV1bV9nZW51cyRTZXFJRA0KaW5jcmVhc2VkIDwtIHBzY19lZmZlY3RfaWxldW1fZ2VudXMkQVNWW3BzY19lZmZlY3RfaWxldW1fZ2VudXMkbG9nMkZvbGRDaGFuZ2UgPjBdDQpkZWNyZWFzZWQgPC0gcHNjX2VmZmVjdF9pbGV1bV9nZW51cyRBU1ZbcHNjX2VmZmVjdF9pbGV1bV9nZW51cyRsb2cyRm9sZENoYW5nZSA8MF0NCmBgYA0KDQoqKkluY3JlYXNlZCArIGRlY3JlYXNlZCAqKg0KDQpgYGB7cn0NCm15X3RhYmxlIDwtIGZpbHRfaWxldW1fZ2VudXNfdGFiICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlNlcUlEIikNCmRhdGFfY2xyIDwtIHZlZ2FuOjpkZWNvc3RhbmQobXlfdGFibGUsbWV0aG9kID0gImNsciIsIE1BUkdJTiA9IDIscHNldWRvY291bnQ9MC41KSAlPiUgYXMubWF0cml4KCkNCmRhdGFfY2xyIDwtIGRhdGFfY2xyW3BzY190YXhhLF0gJT4lIHQoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZUlEIikNCg0KbWV0YWRhdGFfd2l0aF9hYnVuZGFuY2VzIDwtIGRhdGFfY2xyICU+JSBmdWxsX2pvaW4obWV0YWRhdGFfaWxldW0sYnk9IlNhbXBsZUlEIikNCmBgYA0KDQpgYGB7cn0NCiMgc2lnbmlmaWNhbnQgdmFyaWFibGVzDQp2YXJpYWJsZXMgPC0gYygiQUxQIiwNCiAgICAgICAgICAgICAgICJHR1QiLA0KICAgICAgICAgICAgICAgIkFQUklfc2NvcmUiLCANCiAgICAgICAgICAgICAgICJGSUI0X3Njb3JlIiwNCiAgICAgICAgICAgICAgICJBbGJ1bWluIiwNCiAgICAgICAgICAgICAgICJQbGF0ZWxldHMiKQ0KDQpgYGANCg0KYGBge3J9DQpjb3Jyc19pbGV1bSA8LSBjb3Jyc1tncmVwbCgiaWxldW1fZ2VudXMiLG5hbWVzKGNvcnJzKSldDQpjb3Jyc19jb2xvbiA8LSBjb3Jyc1tncmVwbCgiY29sb25fZ2VudXMiLG5hbWVzKGNvcnJzKSldDQpgYGANCg0KYGBge3IsZmlnLndpZHRoPTMsZmlnLmhlaWdodD03fQ0KcHJlcGFyZWRfbGlzdCA8LSBwcmVwYXJlX2Zvcl9oZWF0bWFwKGNvcnJzX2lsZXVtLGNvcnJzX2NvbG9uKQ0KcF9kZl9zaWdfbWRpIDwtIHByZXBhcmVkX2xpc3RbWzFdXQ0KDQp2YXJpYWJsZXMgPC0gY29sbmFtZXMocF9kZl9zaWdfbWRpKVshYXBwbHkocF9kZl9zaWdfbWRpLDIsZnVuY3Rpb24oeCkgew0KICBhbGwoeCA9PSAiIil9KV0NCmBgYA0KDQoNCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0zfQ0KY29ycnMgPC0gbGlzdCgpDQpjbGluaWNhbF9wbG90cyA8LSBsaXN0KCkNCg0KZm9yIChjbGluaWNhbF92YXJpYWJsZSBpbiB2YXJpYWJsZXMpew0KICBmb3IgKHRheG9uIGluIHBzY190YXhhKXsNCiAgICBpZiAoZ3JlcGwoImxvZ18iLGNsaW5pY2FsX3ZhcmlhYmxlKSkgew0KICAgbWV0YWRhdGFfd2l0aF9hYnVuZGFuY2VzWyxjbGluaWNhbF92YXJpYWJsZV0gPC0gbG9nKGFzLm51bWVyaWMobWV0YWRhdGFfd2l0aF9hYnVuZGFuY2VzWyxnc3ViKCJsb2dfIiwiIixjbGluaWNhbF92YXJpYWJsZSldKSkNCiAgfQ0KICANCiAgIyBJTEVVTQ0KICAjIGNvcnJlbGF0aW9uDQogIGNvcnIgPC0gY2xpbmljYWxfY29ycmVsYXRpb25fYWJ1bmRhbmNlcyhtZXRhZGF0YV93aXRoX2FidW5kYW5jZXMsY2xpbmljYWxfdmFyaWFibGUsdGF4b24sbGV2ZWw9ImdlbnVzIikNCiAgY29ycnNbW3Bhc3RlMCgiaWxldW1fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSwiXyIsIHRheG9uKV1dIDwtIGNvcnINCiAgaWYgKGNvcnIkUCA8MC4wNSl7DQogICAgcCA8LSBjbGluaWNhbF9zY2F0dGVyX2FidW5kYW5jZXMoY29ycixtZXRhZGF0YV93aXRoX2FidW5kYW5jZXMsY2xpbmljYWxfdmFyaWFibGUsdGF4b24sbGV2ZWw9ImdlbnVzIixzaXplID0gMykNCiAgY2xpbmljYWxfcGxvdHNbW3Bhc3RlMCgiaWxldW1fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSwiXyIsdGF4b24pXV0gPC0gcA0KICB9IGVsc2UgcHJpbnQoY29yciRQKQ0KICB9DQogIA0KfQ0KYGBgDQoNCmBgYHtyfQ0KcHJlcGFyZWRfbGlzdCA8LSBzdWJwcmVwYXJlX2Zvcl9oZWF0bWFwKGNvcnJzLE1ESSA9IEZBTFNFKQ0KcF9kZl9zaWcgPC0gcHJlcGFyZWRfbGlzdFtbMV1dDQpyX2RmIDwtIHByZXBhcmVkX2xpc3RbWzJdXQ0KYGBgDQoNCmBgYHtyfQ0Kcl9kZl9tZWx0IDwtIG1lbHQocl9kZiAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJTZXFJRCIpKQ0KcF9kZl9tZWx0IDwtIG1lbHQocF9kZl9zaWcgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiU2VxSUQiKSxpZC52YXJzID0gYygiU2VxSUQiKSkNCg0Kcl9kZl9tZWx0JHZhcmlhYmxlIDwtIGZhY3RvcihyX2RmX21lbHQkdmFyaWFibGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9cmV2KHVuaXF1ZShyX2RmX21lbHQkdmFyaWFibGUpKSkNCnBfZGZfbWVsdCR2YXJpYWJsZSA8LSBmYWN0b3IocF9kZl9tZWx0JHZhcmlhYmxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPXJldih1bmlxdWUocF9kZl9tZWx0JHZhcmlhYmxlKSkpDQoNCnJfZGZfbWVsdCRTZXFJRCA8LSBmYWN0b3Iocl9kZl9tZWx0JFNlcUlELCBsZXZlbHMgPSB1bmlxdWUocl9kZl9tZWx0JFNlcUlEKSkNCnBfZGZfbWVsdCRTZXFJRCA8LSBmYWN0b3IocF9kZl9tZWx0JFNlcUlELCBsZXZlbHMgPSB1bmlxdWUocF9kZl9tZWx0JFNlcUlEKSkNCg0KcF9pbGV1bSA8LSBnZ3Bsb3QoKSArIA0KICBnZW9tX3RpbGUoZGF0YT1yX2RmX21lbHQsIGFlcyh2YXJpYWJsZSwgU2VxSUQsIGZpbGw9IHZhbHVlKSkgICsgDQogIHRoZW1lX21pbmltYWwoKSArIA0KICBzY2FsZV9maWxsX2dyYWRpZW50MihuYW1lID0gIlJobyIsIGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiLCBtaWRwb2ludCA9IDAsbGltaXRzID0gYygtMC41LCAwLjUpKSArIA0KICBnZW9tX3RleHQoZGF0YT1wX2RmX21lbHQsYWVzKHg9dmFyaWFibGUseT1TZXFJRCxsYWJlbD12YWx1ZSx2anVzdD0wLjYpKSArIA0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgDQogIHhsYWIoIiIpICsgeWxhYigiIikgICsgDQogIGdndGl0bGUoIlRlcm1pbmFsIGlsZXVtIikrIA0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSxmYWNlID0gImJvbGQiLHNpemUgPSAxNSkpDQoNCnBfaWxldW0NCmBgYA0KDQojIyBDb2xvbg0KDQojIyMgR2VudXMgbGV2ZWwNCg0KQWdncmVnYXRpb24sIGZpbHRlcmluZw0KDQpgYGB7cn0NCmdlbnVzX2RhdGEgPC0gYWdncmVnYXRlX3RheGEoY29sb25fYXN2X3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb25fdGF4YV90YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRheG9ub21pY19sZXZlbD0iR2VudXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcz1UUlVFKQ0KDQpmaWx0X2NvbG9uX2dlbnVzX3RhYiA8LSBnZW51c19kYXRhW1sxXV0NCmZpbHRfY29sb25fZ2VudXNfdGF4YSA8LSBnZW51c19kYXRhW1syXV0NCmZpbHRfY29sb25fbWV0YWRhdGEgPC0gY29sb25fbWV0YWRhdGENCmBgYA0KDQpgYGB7cn0NCnBzY19lZmZlY3RfY29sb25fZ2VudXMgPC0gcmVhZC54bHN4KCIuLi9yZXN1bHRzL1ExX2N6ZWNoL3VuaXZhcmlhdGVfYW5hbHlzaXMvc3VwcGxlbWVudHNfcHNjX2VmZmVjdF9jb2xvbi54bHN4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gImNvbG9uIGdlbnVzIikNCg0KcHNjX3RheGEgPC0gcHNjX2VmZmVjdF9jb2xvbl9nZW51cyRTZXFJRA0KaW5jcmVhc2VkIDwtIHBzY19lZmZlY3RfY29sb25fZ2VudXMkU2VxSURbcHNjX2VmZmVjdF9jb2xvbl9nZW51cyRsb2cyRm9sZENoYW5nZSA+MF0NCmRlY3JlYXNlZCA8LSBwc2NfZWZmZWN0X2NvbG9uX2dlbnVzJFNlcUlEW3BzY19lZmZlY3RfY29sb25fZ2VudXMkbG9nMkZvbGRDaGFuZ2UgPDBdDQpgYGANCg0KDQpgYGB7cn0NCm15X3RhYmxlIDwtIGZpbHRfY29sb25fZ2VudXNfdGFiICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoIlNlcUlEIikNCmRhdGFfY2xyIDwtIHZlZ2FuOjpkZWNvc3RhbmQobXlfdGFibGUsbWV0aG9kID0gImNsciIsIE1BUkdJTiA9IDIscHNldWRvY291bnQ9MC41KSAlPiUgYXMubWF0cml4KCkNCmRhdGFfY2xyIDwtIGRhdGFfY2xyW3BzY190YXhhLF0gJT4lIHQoKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZUlEIikNCg0KbWV0YWRhdGFfd2l0aF9hYnVuZGFuY2VzIDwtIGRhdGFfY2xyICU+JSBmdWxsX2pvaW4obWV0YWRhdGFfY29sb24sYnk9IlNhbXBsZUlEIikNCmBgYA0KDQpgYGB7cn0NCnZhcmlhYmxlcyA8LSBjKCJBTFAiLA0KICAgICAgICAgICAgICAgIkdHVCIsDQogICAgICAgICAgICAgICAiQVBSSV9zY29yZSIsIA0KICAgICAgICAgICAgICAgIkZJQjRfc2NvcmUiLA0KICAgICAgICAgICAgICAgIkFsYnVtaW4iLA0KICAgICAgICAgICAgICAgIlBsYXRlbGV0cyIpDQpgYGANCg0KDQpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9M30NCmNvcnJzIDwtIGxpc3QoKQ0KY2xpbmljYWxfcGxvdHMgPC0gbGlzdCgpDQoNCmZvciAoY2xpbmljYWxfdmFyaWFibGUgaW4gdmFyaWFibGVzKXsNCiAgZm9yICh0YXhvbiBpbiBwc2NfdGF4YSl7DQogICAgaWYgKGdyZXBsKCJsb2dfIixjbGluaWNhbF92YXJpYWJsZSkpIHsNCiAgIG1ldGFkYXRhX3dpdGhfYWJ1bmRhbmNlc1ssY2xpbmljYWxfdmFyaWFibGVdIDwtIGxvZyhhcy5udW1lcmljKG1ldGFkYXRhX3dpdGhfYWJ1bmRhbmNlc1ssZ3N1YigibG9nXyIsIiIsY2xpbmljYWxfdmFyaWFibGUpXSkpDQogIH0NCiAgDQogICMgSUxFVU0NCiAgIyBjb3JyZWxhdGlvbg0KICBjb3JyIDwtIGNsaW5pY2FsX2NvcnJlbGF0aW9uX2FidW5kYW5jZXMobWV0YWRhdGFfd2l0aF9hYnVuZGFuY2VzLGNsaW5pY2FsX3ZhcmlhYmxlLHRheG9uLGxldmVsPSJnZW51cyIpDQogIGNvcnJzW1twYXN0ZTAoImNvbG9uX2dlbnVzXyIsY2xpbmljYWxfdmFyaWFibGUsIl8iLCB0YXhvbildXSA8LSBjb3JyDQogIGlmICggKGNvcnIkUCA9PSAiPCAwLjA1IikgfCAoY29yciRQID09ICI8IDAuMDEiKSB8IChjb3JyJFAgPT0gIjwgMC4wMSIpICl7DQogICAgcCA8LSBjbGluaWNhbF9zY2F0dGVyX2FidW5kYW5jZXMoY29ycixtZXRhZGF0YV93aXRoX2FidW5kYW5jZXMsY2xpbmljYWxfdmFyaWFibGUsdGF4b24sbGV2ZWw9ImdlbnVzIixzaXplID0gMykNCiAgY2xpbmljYWxfcGxvdHNbW3Bhc3RlMCgiY29sb25fZ2VudXNfIixjbGluaWNhbF92YXJpYWJsZSwiXyIsdGF4b24pXV0gPC0gcA0KICB9IGVsc2UgcHJpbnQoY29yciRQKQ0KfQ0KICANCg0KfQ0KYGBgDQoNCmBgYHtyfQ0KY2xpbmljYWxfcGxvdHNfY29sb24gPC0gY2xpbmljYWxfcGxvdHMNCmNvcnJzX2NvbG9uIDwtIGNvcnJzDQpgYGANCg0KYGBge3J9DQpwcmVwYXJlZF9saXN0IDwtIHN1YnByZXBhcmVfZm9yX2hlYXRtYXAoY29ycnMsTURJID0gRkFMU0UpDQpwX2RmX3NpZyA8LSBwcmVwYXJlZF9saXN0W1sxXV0NCnJfZGYgPC0gcHJlcGFyZWRfbGlzdFtbMl1dDQpgYGANCg0KYGBge3J9DQpyX2RmX21lbHQgPC0gbWVsdChyX2RmICU+JSByb3duYW1lc190b19jb2x1bW4oIlNlcUlEIikpDQpwX2RmX21lbHQgPC0gbWVsdChwX2RmX3NpZyAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJTZXFJRCIpLGlkLnZhcnMgPSBjKCJTZXFJRCIpKQ0KDQpyX2RmX21lbHQkdmFyaWFibGUgPC0gZmFjdG9yKHJfZGZfbWVsdCR2YXJpYWJsZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1yZXYodW5pcXVlKHJfZGZfbWVsdCR2YXJpYWJsZSkpKQ0KcF9kZl9tZWx0JHZhcmlhYmxlIDwtIGZhY3RvcihwX2RmX21lbHQkdmFyaWFibGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9cmV2KHVuaXF1ZShwX2RmX21lbHQkdmFyaWFibGUpKSkNCg0Kcl9kZl9tZWx0JFNlcUlEIDwtIGZhY3RvcihyX2RmX21lbHQkU2VxSUQsIGxldmVscyA9IHVuaXF1ZShyX2RmX21lbHQkU2VxSUQpKQ0KcF9kZl9tZWx0JFNlcUlEIDwtIGZhY3RvcihwX2RmX21lbHQkU2VxSUQsIGxldmVscyA9IHVuaXF1ZShwX2RmX21lbHQkU2VxSUQpKQ0KDQpwX2NvbG9uIDwtIGdncGxvdCgpICsgDQogIGdlb21fdGlsZShkYXRhPXJfZGZfbWVsdCwgYWVzKHZhcmlhYmxlLCBTZXFJRCwgZmlsbD0gdmFsdWUpKSAgKyANCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKG5hbWUgPSAiUmhvIiwgbG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIsIG1pZHBvaW50ID0gMCxsaW1pdHMgPSBjKC0wLjUsIDAuNSkpICsgDQogIGdlb21fdGV4dChkYXRhPXBfZGZfbWVsdCxhZXMoeD12YXJpYWJsZSx5PVNlcUlELGxhYmVsPXZhbHVlLHZqdXN0PTAuNikpICsgDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKyANCiAgeGxhYigiIikgKyB5bGFiKCIiKSAgKyANCiAgZ2d0aXRsZSgiQ29sb24iKSsgDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41LGZhY2UgPSAiYm9sZCIsc2l6ZSA9IDE1KSkNCg0KcF9jb2xvbg0KYGBgDQoNCiMjIGhlYXRtYXAgcGxvdA0KDQpgYGB7cixmaWcud2lkdGg9MTUsZmlnLmhlaWdodD01fQ0KZ2dhcnJhbmdlKHBfbWRpLHBfaWxldW0scF9jb2xvbixuY29sID0gMyx3aWR0aHMgPSBjKDAuNywxLDEpKQ0KYGBgDQoNCg0KIyBQQ0EgYW5kIGNsaW5pY2FsIHZhcmlhYmxlcyBmaXQNCg0KIyMgVGVybWluYWwgaWxldW0NCg0KKipBbmFseXNpcyoqDQoNCmBgYHtyfQ0Kc2VnbWVudD0idGVybWluYWxfaWxldW0iDQpgYGANCg0KIyMjIE1haW4gYW5hbHlzaXMgLSBHZW51cywgQWl0Y2hpc29uDQoNCioqR2VudXMgbGV2ZWwsIEFpdGNoaXNvbiBkaXN0YW5jZSoqDQoNCmBgYHtyfQ0KbGV2ZWw9ImdlbnVzIg0KYGBgDQoNCkFnZ3JlZ2F0aW9uLCBmaWx0ZXJpbmcNCg0KYGBge3J9DQojIEFnZ3JlZ2F0aW9uDQpnZW51c19kYXRhIDwtIGFnZ3JlZ2F0ZV90YXhhKGlsZXVtX2Fzdl90YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlsZXVtX3RheGFfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXhvbm9taWNfbGV2ZWw9bGV2ZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzPVRSVUUpDQojIEZpbHRyYXRpb24NCmZpbHRfZGF0YSA8LSBmaWx0ZXJpbmdfc3RlcHMoZ2VudXNfZGF0YVtbMV1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW51c19kYXRhW1syXV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlsZXVtX21ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXFfZGVwdGhfdGhyZXNob2xkPTEwMDAwKQ0KDQpmaWx0X2lsZXVtX2dlbnVzX3RhYiA8LSBmaWx0X2RhdGFbWzFdXQ0KZmlsdF9pbGV1bV9nZW51c190YXhhIDwtIGZpbHRfZGF0YVtbMl1dDQpmaWx0X2lsZXVtX21ldGFkYXRhX2dlbnVzIDwtIGZpbHRfZGF0YVtbM11dDQpgYGANCg0KKipQbG90cyoqDQoNCg0KYGBge3J9DQpwIDwtIHBjYV9wbG90X2N1c3RvbShmaWx0X2lsZXVtX2dlbnVzX3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfaWxldW1fZ2VudXNfdGF4YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfaWxldW1fbWV0YWRhdGFfZ2VudXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2JveHBsb3RzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gIkdyb3VwIiwgc2l6ZT0zLCANCiAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbF9tZXRhZGF0YSA9IG1ldGFkYXRhX2N6KQ0KDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0KDQojIyMgU3VwcGxlbWVudGFyeSBhbmFseXNpcw0KDQojIyMjIEdlbnVzIGxldmVsDQoNCmBgYHtyfQ0KbGV2ZWw9ImdlbnVzIg0KYGBgDQoNCiMjIyMjIEJyYXktQ3VydGlzDQoNCioqUGxvdHMqKg0KDQpgYGB7cix3YXJuaW5nPUZBTFNFfQ0KcCA8LSBwY2FfcGxvdF9jdXN0b20oZmlsdF9pbGV1bV9nZW51c190YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2lsZXVtX2dlbnVzX3RheGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2lsZXVtX21ldGFkYXRhX2dlbnVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZSA9ICJicmF5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfYm94cGxvdHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAiR3JvdXAiLCBzaXplPTMsIHNob3dfbGVnZW5kPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfbWV0YWRhdGE9bWV0YWRhdGFfY3opDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0KIyMjIyMgSmFjY2FyZA0KDQoqKlBsb3RzKioNCg0KDQpgYGB7cn0NCnAgPC0gcGNhX3Bsb3RfY3VzdG9tKGZpbHRfaWxldW1fZ2VudXNfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9pbGV1bV9nZW51c190YXhhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9pbGV1bV9tZXRhZGF0YV9nZW51cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYXN1cmUgPSAiamFjY2FyZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2JveHBsb3RzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gIkdyb3VwIiwgc2l6ZT0zLCBzaG93X2xlZ2VuZD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWw9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX21ldGFkYXRhID0gbWV0YWRhdGFfY3opDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0KIyMjIyBBU1YgbGV2ZWwNCg0KYGBge3J9DQpsZXZlbD0iQVNWIg0KYGBgDQoNCiMjIyMjIEFpdGNoaXNvbiANCg0KKipQQ29BKioNCg0KYGBge3J9DQpwIDwtIHBjYV9wbG90X2N1c3RvbShmaWx0X2lsZXVtX2Fzdl90YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2lsZXVtX3RheGFfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9pbGV1bV9tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfYm94cGxvdHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAiR3JvdXAiLCBzaXplPTMsIHNob3dfbGVnZW5kPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfbWV0YWRhdGEgPSBtZXRhZGF0YV9jeikNCg0KDQojIHNlZSB0aGUgcmVzdWx0cw0KcA0KYGBgDQoNCiMjIyMjIEJyYXktQ3VydGlzIA0KDQoqKlBDb0EqKg0KDQpgYGB7cn0NCnAgPC0gcGNhX3Bsb3RfY3VzdG9tKGZpbHRfaWxldW1fYXN2X3RhYiwNCiAgICAgICAgICAgICAgICAgICAgIGZpbHRfaWxldW1fdGF4YV90YWIsDQogICAgICAgICAgICAgICAgICAgICBmaWx0X2lsZXVtX21ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZSA9ICJicmF5IiwNCiAgICAgICAgICAgICAgICAgICAgIHNob3dfYm94cGxvdHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAiR3JvdXAiLCBzaXplPTMsIHNob3dfbGVnZW5kPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfbWV0YWRhdGE9bWV0YWRhdGFfY3opDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0KIyMjIyMgSmFjY2FyZA0KDQoqKlBDb0EqKg0KDQpgYGB7cn0NCnAgPC0gcGNhX3Bsb3RfY3VzdG9tKGZpbHRfaWxldW1fYXN2X3RhYiwNCiAgICAgICAgICAgICAgICAgICAgIGZpbHRfaWxldW1fdGF4YV90YWIsDQogICAgICAgICAgICAgICAgICAgICBmaWx0X2lsZXVtX21ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZSA9ICJqYWNjYXJkIiwNCiAgICAgICAgICAgICAgICAgICAgIHNob3dfYm94cGxvdHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAiR3JvdXAiLCBzaXplPTMsIHNob3dfbGVnZW5kPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfbWV0YWRhdGE9bWV0YWRhdGFfY3opDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0KIyMgQ29sb24NCg0KYGBge3J9DQpzZWdtZW50PSJjb2xvbiINCmBgYA0KDQoqKkZpbHRlcmluZyoqDQoNClJ1bGVzOiAtIHByZXZhbGVuY2UgXD4gNSUgKHBlciBncm91cCkgLSBuZWFyWmVyb1ZhciB3aXRoIGRlZmF1bHQNCnNldHRpbmdzIC0gc2VxdWVuY2luZyBkZXB0aCBcPiA1MDAwIC0gdGF4b25vbWljIGFzc2lnbm1lbnQgYXQgbGVhc3QNCm9yZGVyDQoNCipTZXF1ZW5jaW5nIGRlcHRoKg0KDQpgYGB7cn0NCmRhdGFfZmlsdCA8LSBzZXFfZGVwdGhfZmlsdGVyaW5nKGNvbG9uX2Fzdl90YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvbl90YXhhX3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9uX21ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxX2RlcHRoX3RocmVzaG9sZCA9IDEwMDAwKQ0KDQpmaWx0X2NvbG9uX2Fzdl90YWIgPC0gZGF0YV9maWx0W1sxXV07IGFscGhhX2NvbG9uX2Fzdl90YWIgPC0gZmlsdF9jb2xvbl9hc3ZfdGFiDQpmaWx0X2NvbG9uX3RheGFfdGFiIDwtIGRhdGFfZmlsdFtbMl1dOyBhbHBoYV9jb2xvbl90YXhhX3RhYiA8LSBmaWx0X2NvbG9uX3RheGFfdGFiDQpmaWx0X2NvbG9uX21ldGFkYXRhIDwtIGRhdGFfZmlsdFtbM11dOyBhbHBoYV9jb2xvbl9tZXRhZGF0YSA8LSBmaWx0X2NvbG9uX21ldGFkYXRhDQoNCnNlcV9zdGVwIDwtIGRpbShmaWx0X2NvbG9uX2Fzdl90YWIpWzFdDQpgYGANCg0KKk5lYXJaZXJvVmFyKg0KDQpgYGB7cn0NCmRhdGFfZmlsdCA8LSBuZWFyemVyb3Zhcl9maWx0ZXJpbmcoZmlsdF9jb2xvbl9hc3ZfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX3RheGFfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX21ldGFkYXRhKQ0KDQpmaWx0X2NvbG9uX2Fzdl90YWIgPC0gZGF0YV9maWx0W1sxXV0NCmZpbHRfY29sb25fdGF4YV90YWIgPC0gZGF0YV9maWx0W1syXV0NCm5lYXJ6ZXJvX3N0ZXAgPC0gZGltKGZpbHRfY29sb25fYXN2X3RhYilbMV0NCmBgYA0KDQoqQ2hlY2sgemVybyBkZXB0aCoNCg0KYGBge3J9DQpkYXRhX2ZpbHQgPC0gY2hlY2tfemVyb19kZXB0aChmaWx0X2NvbG9uX2Fzdl90YWIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9jb2xvbl90YXhhX3RhYiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX21ldGFkYXRhKQ0KDQpmaWx0X2NvbG9uX2Fzdl90YWIgPC0gZGF0YV9maWx0W1sxXV07IA0KZmlsdF9jb2xvbl90YXhhX3RhYiA8LSBkYXRhX2ZpbHRbWzJdXTsgDQpmaWx0X2NvbG9uX21ldGFkYXRhIDwtIGRhdGFfZmlsdFtbM11dOyANCmBgYA0KDQpDYWxjdWxhdGluZyBBaXRjaGlzb24gZGlzdGFuY2UgKGV1Y2xpZGVhbiBkaXN0YW5jZSBvbiBjbHItdHJhbnNmb3JtZWQNCmRhdGEpLCBib3RoIGF0IEFTViBhbmQgZ2VudXMgbGV2ZWwuDQoNCiMjIyBNYWluIGFuYWx5c2lzIC0gR2VudXMsIEFpdGNoaXNvbg0KDQoqKkdlbnVzIGxldmVsLCBBaXRjaGlzb24gZGlzdGFuY2UqKg0KDQpgYGB7cn0NCmxldmVsPSJnZW51cyINCmBgYA0KDQpBZ2dyZWdhdGlvbiwgZmlsdGVyaW5nDQoNCmBgYHtyfQ0KIyBBZ2dyZWdhdGlvbg0KZ2VudXNfZGF0YSA8LSBhZ2dyZWdhdGVfdGF4YShjb2xvbl9hc3ZfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvbl90YXhhX3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGF4b25vbWljX2xldmVsPWxldmVsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcz1UUlVFKQ0KIyBGaWx0cmF0aW9uDQpmaWx0X2RhdGEgPC0gZmlsdGVyaW5nX3N0ZXBzKGdlbnVzX2RhdGFbWzFdXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VudXNfZGF0YVtbMl1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvbl9tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxX2RlcHRoX3RocmVzaG9sZD0xMDAwMCkNCg0KZmlsdF9jb2xvbl9nZW51c190YWIgPC0gZmlsdF9kYXRhW1sxXV0NCmZpbHRfY29sb25fZ2VudXNfdGF4YSA8LSBmaWx0X2RhdGFbWzJdXQ0KZmlsdF9jb2xvbl9nZW51c19tZXRhZGF0YSA8LSBmaWx0X2RhdGFbWzNdXQ0KYGBgDQoNCioqUENvQSoqDQoNCmBgYHtyfQ0KcCA8LSBwY2FfcGxvdF9jdXN0b20oZmlsdF9jb2xvbl9nZW51c190YWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX2dlbnVzX3RheGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX2dlbnVzX21ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19ib3hwbG90cyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJHcm91cCIsIHNpemU9Mywgc2hvd19sZWdlbmQ9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsPVRSVUUsIGNsaW5pY2FsX21ldGFkYXRhID0gbWV0YWRhdGFfY3opDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0KIyMjIFN1cHBsZW1lbnRhcnkgYW5hbHlzaXMNCg0KIyMjIyBHZW51cyBsZXZlbA0KDQpgYGB7cn0NCmxldmVsPSJnZW51cyINCmBgYA0KDQojIyMjIyBCcmF5LUN1cnRpcw0KDQoqKlBsb3RzKioNCg0KYGBge3J9DQpwIDwtIHBjYV9wbG90X2N1c3RvbShmaWx0X2NvbG9uX2dlbnVzX3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfY29sb25fZ2VudXNfdGF4YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfY29sb25fZ2VudXNfbWV0YWRhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlID0gImJyYXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19ib3hwbG90cyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSA9ICJHcm91cCIsIHNpemU9Mywgc2hvd19sZWdlbmQ9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsPVRSVUUsIGNsaW5pY2FsX21ldGFkYXRhID0gbWV0YWRhdGFfY3opDQoNCg0KIyBzZWUgdGhlIHJlc3VsdHMNCnANCmBgYA0KDQojIyMjIyBKYWNjYXJkDQoNCioqUGxvdHMqKg0KDQpgYGB7cn0NCnAgPC0gcGNhX3Bsb3RfY3VzdG9tKGZpbHRfY29sb25fZ2VudXNfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9jb2xvbl9nZW51c190YXhhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9jb2xvbl9nZW51c19tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYXN1cmUgPSAiamFjY2FyZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2JveHBsb3RzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gIkdyb3VwIiwgc2l6ZT0zLCBzaG93X2xlZ2VuZD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWw9VFJVRSwgY2xpbmljYWxfbWV0YWRhdGEgPSBtZXRhZGF0YV9jeikNCg0KDQojIHNlZSB0aGUgcmVzdWx0cw0KcA0KYGBgDQoNCiMjIyMgQVNWIGxldmVsDQoNCmBgYHtyfQ0KbGV2ZWw9IkFTViINCmBgYA0KDQojIyMjIyBBaXRjaGlzb24gDQoNCioqUENvQSoqDQoNCmBgYHtyfQ0KcCA8LSBwY2FfcGxvdF9jdXN0b20oZmlsdF9jb2xvbl9hc3ZfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdF9jb2xvbl90YXhhX3RhYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRfY29sb25fbWV0YWRhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2JveHBsb3RzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gIkdyb3VwIiwgc2l6ZT0zLCBzaG93X2xlZ2VuZD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWw9VFJVRSwgY2xpbmljYWxfbWV0YWRhdGEgPSBtZXRhZGF0YV9jeikNCg0KDQojIHNlZSB0aGUgcmVzdWx0cw0KcA0KYGBgDQoNCiMjIyMjIEJyYXktQ3VydGlzIA0KDQoqKlBDb0EqKg0KDQpgYGB7cn0NCnAgPC0gcGNhX3Bsb3RfY3VzdG9tKGZpbHRfY29sb25fYXN2X3RhYiwNCiAgICAgICAgICAgICAgICAgICAgIGZpbHRfY29sb25fdGF4YV90YWIsDQogICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX21ldGFkYXRhLA0KICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZSA9ICJicmF5IiwNCiAgICAgICAgICAgICAgICAgICAgIHNob3dfYm94cGxvdHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPSAiR3JvdXAiLCBzaXplPTMsIHNob3dfbGVnZW5kPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICBjbGluaWNhbD1UUlVFLCBjbGluaWNhbF9tZXRhZGF0YSA9IG1ldGFkYXRhX2N6KQ0KDQojIHNlZSB0aGUgcmVzdWx0cw0KcA0KYGBgDQoNCiMjIyMjIEphY2NhcmQNCg0KKipQQ29BKioNCg0KYGBge3J9DQpwIDwtIHBjYV9wbG90X2N1c3RvbShmaWx0X2NvbG9uX2Fzdl90YWIsDQogICAgICAgICAgICAgICAgICAgICBmaWx0X2NvbG9uX3RheGFfdGFiLA0KICAgICAgICAgICAgICAgICAgICAgZmlsdF9jb2xvbl9tZXRhZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgIG1lYXN1cmUgPSAiamFjY2FyZCIsDQogICAgICAgICAgICAgICAgICAgICBzaG93X2JveHBsb3RzID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gIkdyb3VwIiwgc2l6ZT0zLCBzaG93X2xlZ2VuZD1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsPVRSVUUsIGNsaW5pY2FsX21ldGFkYXRhID0gbWV0YWRhdGFfY3opDQoNCiMgc2VlIHRoZSByZXN1bHRzDQpwDQpgYGANCg0K